From 2b84e445d598894c711278afacfbb9247333bdd8 Mon Sep 17 00:00:00 2001 From: Jim Fehlig Date: Thu, 10 Feb 2011 15:42:34 -0700 Subject: [PATCH] Add libxenlight driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new xen driver based on libxenlight [1], which is the primary toolstack starting with Xen 4.1.0. The driver is stateful and runs privileged only. Like the existing xen-unified driver, the libxenlight driver is accessed with xen:// URI. Driver selection is based on the status of xend. If xend is running, the libxenlight driver will not load and xen:// connections are handled by xen-unified. If xend is not running *and* the libxenlight driver is available, xen:// connections are deferred to the libxenlight driver. V6: - Address several code style issues noted by Daniel Veillard - Make drive work with xen:/// URI - Hold domain object reference while domain is injected in libvirt event loop. Race found and fixed by Markus Groß. V5: - Ensure events are unregistered when domain private data is destroyed. Discovered and fixed by Markus Groß. V4: - Handle restart of libvirtd, reconnecting to previously started domains - Rebased to current master - Tested against Xen 4.1 RC7-pre (c/s 22961:c5d121fd35c0) V3: - Reserve vnc port within driver when autoport=yes V2: - Update to Xen 4.1 RC6-pre (c/s 22940:5a4710640f81) - Rebased to current master - Plug memory leaks found by Stefano Stabellini and valgrind - Handle SHUTDOWN_crash domain death event [1] http://lists.xensource.com/archives/html/xen-devel/2009-11/msg00436.html --- cfg.mk | 1 + configure.ac | 48 ++ daemon/Makefile.am | 4 + daemon/libvirtd.c | 6 + include/libvirt/virterror.h | 1 + libvirt.spec.in | 15 +- po/POTFILES.in | 2 + src/Makefile.am | 32 + src/driver.h | 3 +- src/libvirt.c | 4 + src/libxl/libxl_conf.c | 898 ++++++++++++++++++++++ src/libxl/libxl_conf.h | 91 +++ src/libxl/libxl_driver.c | 1453 +++++++++++++++++++++++++++++++++++ src/libxl/libxl_driver.h | 27 + src/util/virterror.c | 3 + src/xen/xen_driver.c | 25 + 16 files changed, 2611 insertions(+), 2 deletions(-) create mode 100644 src/libxl/libxl_conf.c create mode 100644 src/libxl/libxl_conf.h create mode 100644 src/libxl/libxl_driver.c create mode 100644 src/libxl/libxl_driver.h diff --git a/cfg.mk b/cfg.mk index c12f199d0d..2076173d45 100644 --- a/cfg.mk +++ b/cfg.mk @@ -403,6 +403,7 @@ msg_gen_function += virXenStoreError msg_gen_function += virXendError msg_gen_function += vmwareError msg_gen_function += xenapiSessionErrorHandler +msg_gen_function += libxlError msg_gen_function += xenUnifiedError msg_gen_function += xenXMError msg_gen_function += VIR_ERROR diff --git a/configure.ac b/configure.ac index e2b2b2447f..9d8862c7c7 100644 --- a/configure.ac +++ b/configure.ac @@ -263,6 +263,8 @@ AC_ARG_WITH([phyp], AC_HELP_STRING([--with-phyp], [add PHYP support @<:@default=check@:>@]),[],[with_phyp=check]) AC_ARG_WITH([xenapi], AC_HELP_STRING([--with-xenapi], [add XenAPI support @<:@default=check@:>@]),[],[with_xenapi=check]) +AC_ARG_WITH([libxl], + AC_HELP_STRING([--with-libxl], [add libxenlight support @<:@default=check@:>@]),[],[with_libxl=check]) AC_ARG_WITH([vbox], AC_HELP_STRING([--with-vbox=@<:@PFX@:>@], [VirtualBox XPCOMC location @<:@default=yes@:>@]),[], @@ -497,6 +499,46 @@ fi AC_SUBST([LIBXENSERVER_CFLAGS]) AC_SUBST([LIBXENSERVER_LIBS]) +old_LIBS="$LIBS" +old_CFLAGS="$CFLAGS" +LIBXL_LIBS="" +LIBXL_CFLAGS="" +dnl search for libxl, aka libxenlight +fail=0 +if test "$with_libxl" != "no" ; then + if test "$with_libxl" != "yes" && test "$with_libxl" != "check" ; then + LIBXL_CFLAGS="-I$with_libxl/include" + LIBXL_LIBS="-L$with_libxl" + fi + CFLAGS="$CFLAGS $LIBXL_CFLAGS" + LIBS="$LIBS $LIBXL_LIBS" + AC_CHECK_LIB([xenlight], [libxl_ctx_init], [ + with_libxl=yes + LIBXL_LIBS="$LIBXL_LIBS -lxenlight -lxenstore -lxenctrl -lxenguest -luuid -lutil -lblktapctl" + ],[ + if test "$with_libxl" = "yes"; then + fail=1 + fi + with_libxl=no + ],[ + -lxenstore -lxenctrl -lxenguest -luuid -lutil -lblktapctl + ]) +fi + +LIBS="$old_LIBS" +CFLAGS="$old_CFLAGS" + +if test $fail = 1; then + AC_MSG_ERROR([You must install the libxl Library to compile libxenlight driver with -lxl]) +fi + +if test "$with_libxl" = "yes"; then + AC_DEFINE_UNQUOTED([WITH_LIBXL], 1, [whether libxenlight driver is enabled]) +fi +AM_CONDITIONAL([WITH_LIBXL], [test "$with_libxl" = "yes"]) + +AC_SUBST([LIBXL_CFLAGS]) +AC_SUBST([LIBXL_LIBS]) old_LIBS="$LIBS" old_CFLAGS="$CFLAGS" @@ -2370,6 +2412,7 @@ AC_MSG_NOTICE([ OpenVZ: $with_openvz]) AC_MSG_NOTICE([ VMware: $with_vmware]) AC_MSG_NOTICE([ VBox: $with_vbox]) AC_MSG_NOTICE([ XenAPI: $with_xenapi]) +AC_MSG_NOTICE([xenlight: $with_libxl]) AC_MSG_NOTICE([ LXC: $with_lxc]) AC_MSG_NOTICE([ PHYP: $with_phyp]) AC_MSG_NOTICE([ ONE: $with_one]) @@ -2479,6 +2522,11 @@ AC_MSG_NOTICE([ xenapi: $LIBXENSERVER_CFLAGS $LIBXENSERVER_LIBS]) else AC_MSG_NOTICE([ xenapi: no]) fi +if test "$with_libxl" = "yes" ; then +AC_MSG_NOTICE([ libxenlight: $LIBXL_CFLAGS $LIBXL_LIBS]) +else +AC_MSG_NOTICE([ libxenlight: no]) +fi if test "$with_hal" = "yes" ; then AC_MSG_NOTICE([ hal: $HAL_CFLAGS $HAL_LIBS]) else diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 15e8129bf5..9e3a5574f1 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -106,6 +106,10 @@ if WITH_LXC libvirtd_LDADD += ../src/libvirt_driver_lxc.la endif +if WITH_LIBXL + libvirtd_LDADD += ../src/libvirt_driver_libxl.la +endif + if WITH_UML libvirtd_LDADD += ../src/libvirt_driver_uml.la endif diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index dc6fab4a51..78183163a5 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -81,6 +81,9 @@ # ifdef WITH_LXC # include "lxc/lxc_driver.h" # endif +# ifdef WITH_LIBXL +# include "libxl/libxl_driver.h" +# endif # ifdef WITH_UML # include "uml/uml_driver.h" # endif @@ -943,6 +946,9 @@ static struct qemud_server *qemudInitialize(void) { # ifdef WITH_NWFILTER nwfilterRegister(); # endif +# ifdef WITH_LIBXL + libxlRegister(); +# endif # ifdef WITH_QEMU qemuRegister(); # endif diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 6b8c789541..1d8275bff5 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -80,6 +80,7 @@ typedef enum { VIR_FROM_STREAMS = 38, /* Error from I/O streams */ VIR_FROM_VMWARE = 39, /* Error from VMware driver */ VIR_FROM_EVENT = 40, /* Error from event loop impl */ + VIR_FROM_LIBXL = 41, /* Error from libxenlight driver */ } virErrorDomain; diff --git a/libvirt.spec.in b/libvirt.spec.in index 1946a153a1..988ea4e1d3 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -45,6 +45,7 @@ %define with_vbox 0%{!?_without_vbox:%{server_drivers}} %define with_uml 0%{!?_without_uml:%{server_drivers}} %define with_xenapi 0%{!?_without_xenapi:%{server_drivers}} +%define with_libxl 0%{!?_without_libxl:%{server_drivers}} # XXX this shouldn't be here, but it mistakenly links into libvirtd %define with_one 0%{!?_without_one:%{server_drivers}} @@ -96,7 +97,7 @@ %endif # RHEL doesn't ship OpenVZ, VBox, UML, OpenNebula, PowerHypervisor, -# VMWare, or libxenserver (xenapi) +# VMWare, libxenserver (xenapi), or libxenlight (Xen 4.1 and newer) %if 0%{?rhel} %define with_openvz 0 %define with_vbox 0 @@ -105,6 +106,7 @@ %define with_phyp 0 %define with_vmware 0 %define with_xenapi 0 +%define with_libxl 0 %endif # RHEL-5 has restricted QEMU to x86_64 only and is too old for LXC @@ -493,6 +495,10 @@ of recent versions of Linux (and other OSes). %define _without_xenapi --without-xenapi %endif +%if ! %{with_libxl} +%define _without_libxl --without-libxl +%endif + %if ! %{with_sasl} %define _without_sasl --without-sasl %endif @@ -872,6 +878,9 @@ fi %dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/qemu/ %dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/lxc/ %dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/uml/ +%if %{with_libxl} +%dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/libxl/ +%endif %config(noreplace) %{_sysconfdir}/logrotate.d/libvirtd %if %{with_qemu} @@ -912,6 +921,10 @@ fi %dir %{_localstatedir}/run/libvirt/uml/ %dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/uml/ %endif +%if %{with_libxl} +%dir %{_localstatedir}/run/libvirt/libxl/ +%dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/libxl/ +%endif %if %{with_network} %dir %{_localstatedir}/run/libvirt/network/ %dir %attr(0700, root, root) %{_localstatedir}/lib/libvirt/network/ diff --git a/po/POTFILES.in b/po/POTFILES.in index 1ed276507e..4bd2b13dfc 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -34,6 +34,8 @@ src/lxc/lxc_conf.c src/lxc/lxc_controller.c src/lxc/lxc_driver.c src/lxc/veth.c +src/libxl/libxl_driver.c +src/libxl/libxl_conf.c src/network/bridge_driver.c src/node_device/node_device_driver.c src/node_device/node_device_hal.c diff --git a/src/Makefile.am b/src/Makefile.am index 645119e70c..c3729a6c8a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -298,6 +298,10 @@ XENAPI_DRIVER_SOURCES = \ xenapi/xenapi_driver_private.h \ xenapi/xenapi_utils.c xenapi/xenapi_utils.h +LIBXL_DRIVER_SOURCES = \ + libxl/libxl_conf.c libxl/libxl_conf.h \ + libxl/libxl_driver.c libxl/libxl_driver.h + UML_DRIVER_SOURCES = \ uml/uml_conf.c uml/uml_conf.h \ uml/uml_driver.c uml/uml_driver.h @@ -692,6 +696,25 @@ endif libvirt_driver_xenapi_la_SOURCES = $(XENAPI_DRIVER_SOURCES) endif +if WITH_LIBXL +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_libxl.la +else +noinst_LTLIBRARIES += libvirt_driver_libxl.la +# Stateful, so linked to daemon instead +#libvirt_la_BUILT_LIBADD += libvirt_driver_libxl.la +endif +libvirt_driver_libxl_la_CFLAGS = $(LIBXL_CFLAGS) \ + -I@top_srcdir@/src/conf $(AM_CFLAGS) +libvirt_driver_libxl_la_LDFLAGS = $(AM_LDFLAGS) +libvirt_driver_libxl_la_LIBADD = $(LIBXL_LIBS) +if WITH_DRIVER_MODULES +libvirt_driver_libxl_la_LIBADD += ../gnulib/lib/libgnu.la +libvirt_driver_libxl_la_LDFLAGS += -module -avoid-version +endif +libvirt_driver_libxl_la_SOURCES = $(LIBXL_DRIVER_SOURCES) +endif + if WITH_QEMU if WITH_DRIVER_MODULES mod_LTLIBRARIES += libvirt_driver_qemu.la @@ -1005,6 +1028,7 @@ EXTRA_DIST += \ $(PHYP_DRIVER_SOURCES) \ $(VBOX_DRIVER_SOURCES) \ $(XENAPI_DRIVER_SOURCES) \ + $(LIBXL_DRIVER_SOURCES) \ $(ESX_DRIVER_SOURCES) \ $(ESX_DRIVER_EXTRA_DIST) \ $(NETWORK_DRIVER_SOURCES) \ @@ -1259,6 +1283,10 @@ if WITH_LXC $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/lxc" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/lxc" endif +if WITH_LIBXL + $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/libxl" + $(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/libxl" +endif if WITH_UML $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/libvirt/uml" $(MKDIR_P) "$(DESTDIR)$(localstatedir)/run/libvirt/uml" @@ -1296,6 +1324,10 @@ if WITH_LXC rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/lxc" ||: rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/lxc" ||: endif +if WITH_LIBXL + rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/libxl" ||: + rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/libxl" ||: +endif if WITH_UML rmdir "$(DESTDIR)$(localstatedir)/lib/libvirt/uml" ||: rmdir "$(DESTDIR)$(localstatedir)/run/libvirt/uml" ||: diff --git a/src/driver.h b/src/driver.h index 6a8332514a..f03d2905dd 100644 --- a/src/driver.h +++ b/src/driver.h @@ -27,7 +27,8 @@ typedef enum { VIR_DRV_ESX = 10, VIR_DRV_PHYP = 11, VIR_DRV_XENAPI = 12, - VIR_DRV_VMWARE = 13 + VIR_DRV_VMWARE = 13, + VIR_DRV_LIBXL = 14, } virDrvNo; diff --git a/src/libvirt.c b/src/libvirt.c index 0ecff74b6b..33bb17cdd1 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -912,6 +912,10 @@ virGetVersion(unsigned long *libVer, const char *type, if (STRCASEEQ(type, "LXC")) *typeVer = LIBVIR_VERSION_NUMBER; # endif +# if WITH_LIBXL + if (STRCASEEQ(type, "xenlight")) + *typeVer = LIBVIR_VERSION_NUMBER; +# endif # if WITH_PHYP if (STRCASEEQ(type, "phyp")) *typeVer = LIBVIR_VERSION_NUMBER; diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c new file mode 100644 index 0000000000..9dc35fcfb9 --- /dev/null +++ b/src/libxl/libxl_conf.c @@ -0,0 +1,898 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * 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 + +#include +#include +#include +#include +#include + +#include "internal.h" +#include "logging.h" +#include "virterror_internal.h" +#include "datatypes.h" +#include "files.h" +#include "memory.h" +#include "uuid.h" +#include "capabilities.h" +#include "libxl_driver.h" +#include "libxl_conf.h" + + +#define VIR_FROM_THIS VIR_FROM_LIBXL + +/* see xen-unstable.hg/xen/include/asm-x86/cpufeature.h */ +#define LIBXL_X86_FEATURE_PAE_MASK 0x40 + + +struct guest_arch { + const char *model; + int bits; + int hvm; + int pae; + int nonpae; + int ia64_be; +}; + +static const char *xen_cap_re = "(xen|hvm)-[[:digit:]]+\\.[[:digit:]]+-(x86_32|x86_64|ia64|powerpc64)(p|be)?"; +static regex_t xen_cap_rec; + + +static int +libxlNextFreeVncPort(libxlDriverPrivatePtr driver, int startPort) +{ + int i; + + for (i = startPort ; i < LIBXL_VNC_PORT_MAX; i++) { + int fd; + int reuse = 1; + struct sockaddr_in addr; + bool used = false; + + if (virBitmapGetBit(driver->reservedVNCPorts, + i - LIBXL_VNC_PORT_MIN, &used) < 0) + VIR_DEBUG("virBitmapGetBit failed on bit %d", i - LIBXL_VNC_PORT_MIN); + + if (used) + continue; + + addr.sin_family = AF_INET; + addr.sin_port = htons(i); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse)) < 0) { + VIR_FORCE_CLOSE(fd); + break; + } + + if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { + /* Not in use, lets grab it */ + VIR_FORCE_CLOSE(fd); + /* Add port to bitmap of reserved ports */ + if (virBitmapSetBit(driver->reservedVNCPorts, + i - LIBXL_VNC_PORT_MIN) < 0) { + VIR_DEBUG("virBitmapSetBit failed on bit %d", + i - LIBXL_VNC_PORT_MIN); + } + return i; + } + VIR_FORCE_CLOSE(fd); + + if (errno == EADDRINUSE) { + /* In use, try next */ + continue; + } + /* Some other bad failure, get out.. */ + break; + } + return -1; +} + +static virCapsPtr +libxlBuildCapabilities(const char *hostmachine, + int host_pae, + struct guest_arch *guest_archs, + int nr_guest_archs) +{ + virCapsPtr caps; + int i; + + if ((caps = virCapabilitiesNew(hostmachine, 1, 1)) == NULL) + goto no_memory; + + virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x00, 0x16, 0x3e }); + + if (host_pae && + virCapabilitiesAddHostFeature(caps, "pae") < 0) + goto no_memory; + + for (i = 0; i < nr_guest_archs; ++i) { + virCapsGuestPtr guest; + char const *const xen_machines[] = {guest_archs[i].hvm ? "xenfv" : "xenpv"}; + virCapsGuestMachinePtr *machines; + + if ((machines = virCapabilitiesAllocMachines(xen_machines, 1)) == NULL) + goto no_memory; + + if ((guest = virCapabilitiesAddGuest(caps, + guest_archs[i].hvm ? "hvm" : "xen", + guest_archs[i].model, + guest_archs[i].bits, + (STREQ(hostmachine, "x86_64") ? + "/usr/lib64/xen/bin/qemu-dm" : + "/usr/lib/xen/bin/qemu-dm"), + (guest_archs[i].hvm ? + "/usr/lib/xen/boot/hvmloader" : + NULL), + 1, + machines)) == NULL) { + virCapabilitiesFreeMachines(machines, 1); + goto no_memory; + } + machines = NULL; + + if (virCapabilitiesAddGuestDomain(guest, + "xen", + NULL, + NULL, + 0, + NULL) == NULL) + goto no_memory; + + if (guest_archs[i].pae && + virCapabilitiesAddGuestFeature(guest, + "pae", + 1, + 0) == NULL) + goto no_memory; + + if (guest_archs[i].nonpae && + virCapabilitiesAddGuestFeature(guest, + "nonpae", + 1, + 0) == NULL) + goto no_memory; + + if (guest_archs[i].ia64_be && + virCapabilitiesAddGuestFeature(guest, + "ia64_be", + 1, + 0) == NULL) + goto no_memory; + + if (guest_archs[i].hvm) { + if (virCapabilitiesAddGuestFeature(guest, + "acpi", + 1, + 1) == NULL) + goto no_memory; + + if (virCapabilitiesAddGuestFeature(guest, "apic", + 1, + 0) == NULL) + goto no_memory; + + if (virCapabilitiesAddGuestFeature(guest, + "hap", + 0, + 1) == NULL) + goto no_memory; + } + } + + caps->defaultConsoleTargetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN; + + return caps; + + no_memory: + virCapabilitiesFree(caps); + return NULL; +} + +static virCapsPtr +libxlMakeCapabilitiesInternal(const char *hostmachine, + libxl_physinfo *phy_info, + char *capabilities) +{ + char *str, *token; + regmatch_t subs[4]; + char *saveptr = NULL; + int i; + + int host_pae = 0; + struct guest_arch guest_archs[32]; + int nr_guest_archs = 0; + virCapsPtr caps = NULL; + + memset(guest_archs, 0, sizeof(guest_archs)); + + /* hw_caps is an array of 32-bit words whose meaning is listed in + * xen-unstable.hg/xen/include/asm-x86/cpufeature.h. Each feature + * is defined in the form X*32+Y, corresponding to the Y'th bit in + * the X'th 32-bit word of hw_cap. + */ + host_pae = phy_info->hw_cap[0] & LIBXL_X86_FEATURE_PAE_MASK; + + /* Format of capabilities string is documented in the code in + * xen-unstable.hg/xen/arch/.../setup.c. + * + * It is a space-separated list of supported guest architectures. + * + * For x86: + * TYP-VER-ARCH[p] + * ^ ^ ^ ^ + * | | | +-- PAE supported + * | | +------- x86_32 or x86_64 + * | +----------- the version of Xen, eg. "3.0" + * +--------------- "xen" or "hvm" for para or full virt respectively + * + * For IA64: + * TYP-VER-ARCH[be] + * ^ ^ ^ ^ + * | | | +-- Big-endian supported + * | | +------- always "ia64" + * | +----------- the version of Xen, eg. "3.0" + * +--------------- "xen" or "hvm" for para or full virt respectively + */ + + /* Split capabilities string into tokens. strtok_r is OK here because + * we "own" the buffer. Parse out the features from each token. + */ + for (str = capabilities, nr_guest_archs = 0; + nr_guest_archs < sizeof(guest_archs) / sizeof(guest_archs[0]) + && (token = strtok_r(str, " ", &saveptr)) != NULL; + str = NULL) { + if (regexec(&xen_cap_rec, token, sizeof(subs) / sizeof(subs[0]), + subs, 0) == 0) { + int hvm = STRPREFIX(&token[subs[1].rm_so], "hvm"); + const char *model; + int bits, pae = 0, nonpae = 0, ia64_be = 0; + + if (STRPREFIX(&token[subs[2].rm_so], "x86_32")) { + model = "i686"; + bits = 32; + if (subs[3].rm_so != -1 && + STRPREFIX(&token[subs[3].rm_so], "p")) + pae = 1; + else + nonpae = 1; + } + else if (STRPREFIX(&token[subs[2].rm_so], "x86_64")) { + model = "x86_64"; + bits = 64; + } + else if (STRPREFIX(&token[subs[2].rm_so], "ia64")) { + model = "ia64"; + bits = 64; + if (subs[3].rm_so != -1 && + STRPREFIX(&token[subs[3].rm_so], "be")) + ia64_be = 1; + } + else if (STRPREFIX(&token[subs[2].rm_so], "powerpc64")) { + model = "ppc64"; + bits = 64; + } else { + continue; + } + + /* Search for existing matching (model,hvm) tuple */ + for (i = 0 ; i < nr_guest_archs ; i++) { + if (STREQ(guest_archs[i].model, model) && + guest_archs[i].hvm == hvm) { + break; + } + } + + /* Too many arch flavours - highly unlikely ! */ + if (i >= ARRAY_CARDINALITY(guest_archs)) + continue; + /* Didn't find a match, so create a new one */ + if (i == nr_guest_archs) + nr_guest_archs++; + + guest_archs[i].model = model; + guest_archs[i].bits = bits; + guest_archs[i].hvm = hvm; + + /* Careful not to overwrite a previous positive + setting with a negative one here - some archs + can do both pae & non-pae, but Xen reports + separately capabilities so we're merging archs */ + if (pae) + guest_archs[i].pae = pae; + if (nonpae) + guest_archs[i].nonpae = nonpae; + if (ia64_be) + guest_archs[i].ia64_be = ia64_be; + } + } + + if ((caps = libxlBuildCapabilities(hostmachine, + host_pae, + guest_archs, + nr_guest_archs)) == NULL) + goto no_memory; + + return caps; + + no_memory: + virReportOOMError(); + virCapabilitiesFree(caps); + return NULL; +} + +static int +libxlMakeDomCreateInfo(virDomainDefPtr def, libxl_domain_create_info *c_info) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + libxl_init_create_info(c_info); + + c_info->hvm = STREQ(def->os.type, "hvm"); + if ((c_info->name = strdup(def->name)) == NULL) { + virReportOOMError(); + goto error; + } + + virUUIDFormat(def->uuid, uuidstr); + if (libxl_uuid_from_string(&c_info->uuid, uuidstr) ) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight failed to parse UUID '%s'"), uuidstr); + goto error; + } + + return 0; + +error: + libxl_domain_create_info_destroy(c_info); + return -1; +} + +static int +libxlMakeDomBuildInfo(virDomainDefPtr def, libxl_domain_config *d_config) +{ + libxl_domain_build_info *b_info = &d_config->b_info; + int hvm = STREQ(def->os.type, "hvm"); + + libxl_init_build_info(b_info, &d_config->c_info); + + b_info->hvm = hvm; + b_info->max_vcpus = def->maxvcpus; + b_info->cur_vcpus = def->vcpus; + if (def->clock.ntimers > 0 && + def->clock.timers[0]->name == VIR_DOMAIN_TIMER_NAME_TSC) { + switch (def->clock.timers[0]->mode) { + case VIR_DOMAIN_TIMER_MODE_NATIVE: + b_info->tsc_mode = 2; + break; + case VIR_DOMAIN_TIMER_MODE_PARAVIRT: + b_info->tsc_mode = 3; + break; + default: + b_info->tsc_mode = 1; + } + } + b_info->max_memkb = def->mem.max_balloon; + b_info->target_memkb = def->mem.cur_balloon; + if (hvm) { + b_info->u.hvm.pae = def->features & (1 << VIR_DOMAIN_FEATURE_PAE); + b_info->u.hvm.apic = def->features & (1 << VIR_DOMAIN_FEATURE_APIC); + b_info->u.hvm.acpi = def->features & (1 << VIR_DOMAIN_FEATURE_ACPI); + /* + * The following comment and calculation were taken directly from + * libxenlight's internal function libxl_get_required_shadow_memory(): + * + * 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M map, + * plus 1 page per MiB of RAM to shadow the resident processes. + */ + b_info->shadow_memkb = 4 * (256 * b_info->cur_vcpus + + 2 * (b_info->max_memkb / 1024)); + } else { + if (def->os.bootloader) { + if ((b_info->u.pv.bootloader = strdup(def->os.bootloader)) == NULL) { + virReportOOMError(); + goto error; + } + } + if (def->os.bootloaderArgs) { + if ((b_info->u.pv.bootloader_args = strdup(def->os.bootloaderArgs)) == NULL) { + virReportOOMError(); + goto error; + } + } + if (def->os.cmdline) { + if ((b_info->u.pv.cmdline = strdup(def->os.cmdline)) == NULL) { + virReportOOMError(); + goto error; + } + } + if (def->os.kernel) { + /* libxl_init_build_info() sets kernel.path = strdup("hvmloader") */ + free(b_info->kernel.path); + if ((b_info->kernel.path = strdup(def->os.kernel)) == NULL) { + virReportOOMError(); + goto error; + } + } + if (def->os.initrd) { + if ((b_info->u.pv.ramdisk.path = strdup(def->os.initrd)) == NULL) { + virReportOOMError(); + goto error; + } + } + } + + return 0; + +error: + libxl_domain_build_info_destroy(b_info); + return -1; +} + +static int +libxlMakeDiskList(virDomainDefPtr def, libxl_domain_config *d_config) +{ + virDomainDiskDefPtr *l_disks = def->disks; + int ndisks = def->ndisks; + libxl_device_disk *x_disks; + int i; + + if (VIR_ALLOC_N(x_disks, ndisks) < 0) { + virReportOOMError(); + return -1; + } + + for (i = 0; i < ndisks; i++) { + if (l_disks[i]->src && + (x_disks[i].pdev_path = strdup(l_disks[i]->src)) == NULL) { + virReportOOMError(); + goto error; + } + + if (l_disks[i]->dst && + (x_disks[i].vdev = strdup(l_disks[i]->dst)) == NULL) { + virReportOOMError(); + goto error; + } + + if (l_disks[i]->driverName) { + if (STREQ(l_disks[i]->driverName, "tap") || + STREQ(l_disks[i]->driverName, "tap2")) { + if (l_disks[i]->driverType) { + if (STREQ(l_disks[i]->driverType, "qcow")) { + x_disks[i].format = DISK_FORMAT_QCOW; + x_disks[i].backend = DISK_BACKEND_QDISK; + } else if (STREQ(l_disks[i]->driverType, "qcow2")) { + x_disks[i].format = DISK_FORMAT_QCOW2; + x_disks[i].backend = DISK_BACKEND_QDISK; + } else if (STREQ(l_disks[i]->driverType, "vhd")) { + x_disks[i].format = DISK_FORMAT_VHD; + x_disks[i].backend = DISK_BACKEND_TAP; + } else if (STREQ(l_disks[i]->driverType, "aio") || + STREQ(l_disks[i]->driverType, "raw")) { + x_disks[i].format = DISK_FORMAT_RAW; + x_disks[i].backend = DISK_BACKEND_TAP; + } + } else { + /* No subtype specified, default to raw/tap */ + x_disks[i].format = DISK_FORMAT_RAW; + x_disks[i].backend = DISK_BACKEND_TAP; + } + } else if (STREQ(l_disks[i]->driverName, "file")) { + x_disks[i].format = DISK_FORMAT_RAW; + x_disks[i].backend = DISK_BACKEND_TAP; + } else if (STREQ(l_disks[i]->driverName, "phy")) { + x_disks[i].format = DISK_FORMAT_RAW; + x_disks[i].backend = DISK_BACKEND_PHY; + } else { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight does not support disk driver %s"), + l_disks[i]->driverName); + goto error; + } + } else { + /* No driverName - default to raw/tap?? */ + x_disks[i].format = DISK_FORMAT_RAW; + x_disks[i].backend = DISK_BACKEND_TAP; + } + + /* How to set unpluggable? */ + x_disks[i].unpluggable = 1; + x_disks[i].readwrite = !l_disks[i]->readonly; + x_disks[i].is_cdrom = + l_disks[i]->device == VIR_DOMAIN_DISK_DEVICE_CDROM ? 1 : 0; + } + + d_config->disks = x_disks; + d_config->num_disks = ndisks; + + return 0; + +error: + for (i = 0; i < ndisks; i++) + libxl_device_disk_destroy(&x_disks[i]); + VIR_FREE(x_disks); + return -1; +} + +static int +libxlMakeNicList(virDomainDefPtr def, libxl_domain_config *d_config) +{ + virDomainNetDefPtr *l_nics = def->nets; + int nnics = def->nnets; + libxl_device_nic *x_nics; + int i; + + if (VIR_ALLOC_N(x_nics, nnics) < 0) { + virReportOOMError(); + return -1; + } + + for (i = 0; i < nnics; i++) { + x_nics[i].devid = i; + + // TODO: Where is mtu stored? + //x_nics[i].mtu = 1492; + + memcpy(x_nics[i].mac, l_nics[i]->mac, sizeof(libxl_mac)); + + if (l_nics[i]->model && !STREQ(l_nics[i]->model, "netfront")) { + if ((x_nics[i].model = strdup(l_nics[i]->model)) == NULL) { + virReportOOMError(); + goto error; + } + x_nics[i].nictype = NICTYPE_IOEMU; + } else { + x_nics[i].nictype = NICTYPE_VIF; + } + + if (l_nics[i]->ifname && + (x_nics[i].ifname = strdup(l_nics[i]->ifname)) == NULL) { + virReportOOMError(); + goto error; + } + + if (l_nics[i]->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { + if (l_nics[i]->data.bridge.brname && + (x_nics[i].bridge = + strdup(l_nics[i]->data.bridge.brname)) == NULL) { + virReportOOMError(); + goto error; + } + if (l_nics[i]->data.bridge.script && + (x_nics[i].script = + strdup(l_nics[i]->data.bridge.script)) == NULL) { + virReportOOMError(); + goto error; + } + } + } + + d_config->vifs = x_nics; + d_config->num_vifs = nnics; + + return 0; + +error: + for (i = 0; i < nnics; i++) + libxl_device_nic_destroy(&x_nics[i]); + VIR_FREE(x_nics); + return -1; +} + +static int +libxlMakeVfbList(libxlDriverPrivatePtr driver, + virDomainDefPtr def, libxl_domain_config *d_config) +{ + virDomainGraphicsDefPtr *l_vfbs = def->graphics; + int nvfbs = def->ngraphics; + libxl_device_vfb *x_vfbs; + libxl_device_vkb *x_vkbs; + int i; + int port; + + if (nvfbs == 0) + return 0; + + if (VIR_ALLOC_N(x_vfbs, nvfbs) < 0) { + virReportOOMError(); + return -1; + } + if (VIR_ALLOC_N(x_vkbs, nvfbs) < 0) { + virReportOOMError(); + VIR_FREE(x_vfbs); + return -1; + } + + for (i = 0; i < nvfbs; i++) { + libxl_device_vfb_init(&x_vfbs[i], i); + libxl_device_vkb_init(&x_vkbs[i], i); + + switch (l_vfbs[i]->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + x_vfbs[i].sdl = 1; + if (l_vfbs[i]->data.sdl.display && + (x_vfbs[i].display = + strdup(l_vfbs[i]->data.sdl.display)) == NULL) { + virReportOOMError(); + goto error; + } + if (l_vfbs[i]->data.sdl.xauth && + (x_vfbs[i].xauthority = + strdup(l_vfbs[i]->data.sdl.xauth)) == NULL) { + virReportOOMError(); + goto error; + } + break; + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + x_vfbs[i].vnc = 1; + /* driver handles selection of free port */ + x_vfbs[i].vncunused = 0; + if (l_vfbs[i]->data.vnc.autoport) { + port = libxlNextFreeVncPort(driver, LIBXL_VNC_PORT_MIN); + if (port < 0) { + libxlError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to find an unused VNC port")); + goto error; + } + l_vfbs[i]->data.vnc.port = port; + } + x_vfbs[i].vncdisplay = l_vfbs[i]->data.vnc.port - + LIBXL_VNC_PORT_MIN; + + if (l_vfbs[i]->data.vnc.listenAddr) { + /* libxl_device_vfb_init() does strdup("127.0.0.1") */ + free(x_vfbs[i].vnclisten); + if ((x_vfbs[i].vnclisten = + strdup(l_vfbs[i]->data.vnc.listenAddr)) == NULL) { + virReportOOMError(); + goto error; + } + } + if (l_vfbs[i]->data.vnc.keymap && + (x_vfbs[i].keymap = + strdup(l_vfbs[i]->data.vnc.keymap)) == NULL) { + virReportOOMError(); + goto error; + } + break; + } + } + + d_config->vfbs = x_vfbs; + d_config->vkbs = x_vkbs; + d_config->num_vfbs = d_config->num_vkbs = nvfbs; + + return 0; + +error: + for (i = 0; i < nvfbs; i++) { + libxl_device_vfb_destroy(&x_vfbs[i]); + libxl_device_vkb_destroy(&x_vkbs[i]); + } + VIR_FREE(x_vfbs); + VIR_FREE(x_vkbs); + return -1; +} + +static int +libxlMakeChrdevStr(virDomainChrDefPtr def, char **buf) +{ + const char *type = virDomainChrTypeToString(def->source.type); + + if (!type) { + libxlError(VIR_ERR_INTERNAL_ERROR, + "%s", _("unexpected chr device type")); + return -1; + } + + switch (def->source.type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + case VIR_DOMAIN_CHR_TYPE_STDIO: + case VIR_DOMAIN_CHR_TYPE_VC: + case VIR_DOMAIN_CHR_TYPE_PTY: + if (virAsprintf(buf, "%s", type) < 0) { + virReportOOMError(); + return -1; + } + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + case VIR_DOMAIN_CHR_TYPE_PIPE: + if (virAsprintf(buf, "%s:%s", type, + def->source.data.file.path) < 0) { + virReportOOMError(); + return -1; + } + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + if (virAsprintf(buf, "%s", def->source.data.file.path) < 0) { + virReportOOMError(); + return -1; + } + break; + } + + return 0; +} + +static int +libxlMakeDeviceModelInfo(virDomainDefPtr def, libxl_domain_config *d_config) +{ + libxl_device_model_info *dm_info = &d_config->dm_info; + int i; + char b_order[VIR_DOMAIN_BOOT_LAST+1]; + + libxl_init_dm_info(dm_info, &d_config->c_info, &d_config->b_info); + + if (d_config->b_info.hvm) { + /* HVM-specific device model info */ + dm_info->type = XENFV; + if (def->os.nBootDevs > 0) { + free(dm_info->boot); + for (i = 0; i < def->os.nBootDevs; i++) { + switch (def->os.bootDevs[i]) { + case VIR_DOMAIN_BOOT_FLOPPY: + b_order[i] = 'a'; + break; + default: + case VIR_DOMAIN_BOOT_DISK: + b_order[i] = 'c'; + break; + case VIR_DOMAIN_BOOT_CDROM: + b_order[i] = 'd'; + break; + case VIR_DOMAIN_BOOT_NET: + b_order[i] = 'n'; + break; + } + } + b_order[def->os.nBootDevs] = '\0'; + if ((dm_info->boot = strdup(b_order)) == NULL) { + virReportOOMError(); + goto error; + } + } + if (def->serials && + (libxlMakeChrdevStr(def->serials[0], &dm_info->serial) < 0)) + goto error; + } else { + /* PV-specific device model info */ + dm_info->type = XENPV; + } + + /* Build qemu graphics options from previously parsed vfb */ + if (d_config->num_vfbs > 0) { + if (d_config->vfbs[0].vnc) { + dm_info->vnc = 1; + /* driver handles selection of free port */ + dm_info->vncunused = 0; + if (d_config->vfbs[0].vnclisten) { + free(dm_info->vnclisten); + if ((dm_info->vnclisten = + strdup(d_config->vfbs[0].vnclisten)) == NULL) { + virReportOOMError(); + goto error; + } + } + if (d_config->vfbs[0].keymap && + (dm_info->keymap = strdup(d_config->vfbs[0].keymap)) == NULL) { + virReportOOMError(); + goto error; + } + dm_info->vncdisplay = d_config->vfbs[0].vncdisplay; + if (d_config->vfbs[0].vncpasswd && + (dm_info->vncpasswd = + strdup(d_config->vfbs[0].vncpasswd)) == NULL) { + virReportOOMError(); + goto error; + } + } else if (d_config->vfbs[0].sdl) { + dm_info->sdl = 1; + dm_info->vnc = 0; + } + } else if (d_config->num_vfbs == 0) { + dm_info->nographic = 1; + dm_info->vnc = 0; + } + + // TODO + //dm_info->usb = ; + //dm_info->usbdevice = ; + //dm_info->soundhw = ; + + return 0; + +error: + libxl_device_model_info_destroy(dm_info); + return -1; +} + +virCapsPtr +libxlMakeCapabilities(libxl_ctx *ctx) +{ + libxl_physinfo phy_info; + const libxl_version_info *ver_info; + struct utsname utsname; + + regcomp (&xen_cap_rec, xen_cap_re, REG_EXTENDED); + + if (libxl_get_physinfo(ctx, &phy_info) != 0) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("Failed to get node physical info from libxenlight")); + return NULL; + } + + if ((ver_info = libxl_get_version_info(ctx)) == NULL) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("Failed to get version info from libxenlight")); + return NULL; + } + + uname(&utsname); + + return libxlMakeCapabilitiesInternal(utsname.machine, + &phy_info, + ver_info->capabilities); +} + +int +libxlBuildDomainConfig(libxlDriverPrivatePtr driver, + virDomainDefPtr def, libxl_domain_config *d_config) +{ + + if (libxlMakeDomCreateInfo(def, &d_config->c_info) < 0) + return -1; + + if (libxlMakeDomBuildInfo(def, d_config) < 0) { + goto error; + } + + if (libxlMakeDiskList(def, d_config) < 0) { + goto error; + } + + if (libxlMakeNicList(def, d_config) < 0) { + goto error; + } + + if (libxlMakeVfbList(driver, def, d_config) < 0) { + goto error; + } + + if (libxlMakeDeviceModelInfo(def, d_config) < 0) { + goto error; + } + + d_config->on_reboot = def->onReboot; + d_config->on_poweroff = def->onPoweroff; + d_config->on_crash = def->onCrash; + + return 0; + +error: + libxl_domain_config_destroy(d_config); + return -1; +} diff --git a/src/libxl/libxl_conf.h b/src/libxl/libxl_conf.h new file mode 100644 index 0000000000..bb49d35a1e --- /dev/null +++ b/src/libxl/libxl_conf.h @@ -0,0 +1,91 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * 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 LIBXL_CONF_H +# define LIBXL_CONF_H + +# include + +# include + +# include "internal.h" +# include "domain_conf.h" +# include "capabilities.h" +# include "configmake.h" +# include "bitmap.h" + + +# define LIBXL_VNC_PORT_MIN 5900 +# define LIBXL_VNC_PORT_MAX 65535 + +# define LIBXL_CONFIG_DIR SYSCONFDIR "/libvirt/libxl" +# define LIBXL_AUTOSTART_DIR LIBXL_CONFIG_DIR "/autostart" +# define LIBXL_STATE_DIR LOCALSTATEDIR "/run/libvirt/libxl" +# define LIBXL_LOG_DIR LOCALSTATEDIR "/log/libvirt/libxl" +# define LIBXL_LIB_DIR LOCALSTATEDIR "/lib/libvirt/libxl" +# define LIBXL_SAVE_DIR LIBXL_LIB_DIR "/save" + + +typedef struct _libxlDriverPrivate libxlDriverPrivate; +typedef libxlDriverPrivate *libxlDriverPrivatePtr; +struct _libxlDriverPrivate { + virMutex lock; + virCapsPtr caps; + unsigned int version; + + FILE *logger_file; + xentoollog_logger *logger; + /* libxl ctx for driver wide ops; getVersion, getNodeInfo, ... */ + libxl_ctx ctx; + + virBitmapPtr reservedVNCPorts; + virDomainObjList domains; + + char *configDir; + char *autostartDir; + char *logDir; + char *stateDir; + char *libDir; + char *saveDir; +}; + +typedef struct _libxlDomainObjPrivate libxlDomainObjPrivate; +typedef libxlDomainObjPrivate *libxlDomainObjPrivatePtr; +struct _libxlDomainObjPrivate { + /* per domain libxl ctx */ + libxl_ctx ctx; + libxl_waiter *dWaiter; + int waiterFD; + int eventHdl; +}; + + +# define libxlError(code, ...) \ + virReportErrorHelper(NULL, VIR_FROM_LIBXL, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +virCapsPtr +libxlMakeCapabilities(libxl_ctx *ctx); + +int +libxlBuildDomainConfig(libxlDriverPrivatePtr driver, + virDomainDefPtr def, libxl_domain_config *d_config); + + +#endif /* LIBXL_CONF_H */ diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c new file mode 100644 index 0000000000..615cb47437 --- /dev/null +++ b/src/libxl/libxl_driver.c @@ -0,0 +1,1453 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. + * Copyright (C) 2011 Univention GmbH. + * + * 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 + +#include +#include + +#include "internal.h" +#include "logging.h" +#include "virterror_internal.h" +#include "datatypes.h" +#include "files.h" +#include "memory.h" +#include "event.h" +#include "uuid.h" +#include "command.h" +#include "libxl_driver.h" +#include "libxl_conf.h" + + +#define VIR_FROM_THIS VIR_FROM_LIBXL + +#define LIBXL_DOM_REQ_POWEROFF 0 +#define LIBXL_DOM_REQ_REBOOT 1 +#define LIBXL_DOM_REQ_SUSPEND 2 +#define LIBXL_DOM_REQ_CRASH 3 +#define LIBXL_DOM_REQ_HALT 4 + +static libxlDriverPrivatePtr libxl_driver = NULL; + + +/* Function declarations */ +static int +libxlVmStart(libxlDriverPrivatePtr driver, + virDomainObjPtr vm, bool start_paused); + + +/* Function definitions */ +static void +libxlDriverLock(libxlDriverPrivatePtr driver) +{ + virMutexLock(&driver->lock); +} + +static void +libxlDriverUnlock(libxlDriverPrivatePtr driver) +{ + virMutexUnlock(&driver->lock); +} + +static void * +libxlDomainObjPrivateAlloc(void) +{ + libxlDomainObjPrivatePtr priv; + + if (VIR_ALLOC(priv) < 0) + return NULL; + + libxl_ctx_init(&priv->ctx, LIBXL_VERSION, libxl_driver->logger); + priv->waiterFD = -1; + priv->eventHdl = -1; + + return priv; +} + +static void +libxlDomainObjPrivateFree(void *data) +{ + libxlDomainObjPrivatePtr priv = data; + + if (priv->eventHdl >= 0) + virEventRemoveHandle(priv->eventHdl); + + if (priv->dWaiter) { + libxl_stop_waiting(&priv->ctx, priv->dWaiter); + libxl_free_waiter(priv->dWaiter); + VIR_FREE(priv->dWaiter); + } + + libxl_ctx_free(&priv->ctx); + VIR_FREE(priv); +} + +/* + * Remove reference to domain object. + */ +static void +libxlDomainObjUnref(void *data) +{ + virDomainObjPtr vm = data; + + virDomainObjUnref(vm); +} + +/* + * Cleanup function for domain that has reached shutoff state. + * + * virDomainObjPtr should be locked on invocation + */ +static void +libxlVmCleanup(libxlDriverPrivatePtr driver, virDomainObjPtr vm) +{ + libxlDomainObjPrivatePtr priv = vm->privateData; + int vnc_port; + char *file; + + if (priv->eventHdl >= 0) { + virEventRemoveHandle(priv->eventHdl); + priv->eventHdl = -1; + } + + if (priv->dWaiter) { + libxl_stop_waiting(&priv->ctx, priv->dWaiter); + libxl_free_waiter(priv->dWaiter); + VIR_FREE(priv->dWaiter); + } + + if (vm->persistent) { + vm->def->id = -1; + vm->state = VIR_DOMAIN_SHUTOFF; + } + + if ((vm->def->ngraphics == 1) && + vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && + vm->def->graphics[0]->data.vnc.autoport) { + vnc_port = vm->def->graphics[0]->data.vnc.port; + if (vnc_port >= LIBXL_VNC_PORT_MIN) { + if (virBitmapClearBit(driver->reservedVNCPorts, + vnc_port - LIBXL_VNC_PORT_MIN) < 0) + VIR_DEBUG("Could not mark port %d as unused", vnc_port); + } + } + + if (virAsprintf(&file, "%s/%s.xml", driver->stateDir, vm->def->name) > 0) { + if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR) + VIR_DEBUG("Failed to remove domain XML for %s", vm->def->name); + VIR_FREE(file); + } +} + +/* + * Reap a domain from libxenlight. + * + * virDomainObjPtr should be locked on invocation + */ +static int +libxlVmReap(libxlDriverPrivatePtr driver, virDomainObjPtr vm, int force) +{ + libxlDomainObjPrivatePtr priv = vm->privateData; + + if (libxl_domain_destroy(&priv->ctx, vm->def->id, force) < 0) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("Unable to cleanup domain %d"), vm->def->id); + return -1; + } + + libxlVmCleanup(driver, vm); + return 0; +} + +/* + * Handle previously registered event notification from libxenlight + */ +static void libxlEventHandler(int watch, + int fd, + int events, + void *data) +{ + libxlDriverPrivatePtr driver = libxl_driver; + virDomainObjPtr vm = data; + libxlDomainObjPrivatePtr priv; + libxl_event event; + libxl_dominfo info; + + libxlDriverLock(driver); + virDomainObjLock(vm); + libxlDriverUnlock(driver); + + priv = vm->privateData; + + memset(&event, 0, sizeof(event)); + memset(&info, 0, sizeof(info)); + + if (priv->waiterFD != fd || priv->eventHdl != watch) { + virEventRemoveHandle(watch); + priv->eventHdl = -1; + goto cleanup; + } + + if (!(events & VIR_EVENT_HANDLE_READABLE)) + goto cleanup; + + if (libxl_get_event(&priv->ctx, &event)) + goto cleanup; + + if (event.type == LIBXL_EVENT_DOMAIN_DEATH) { + /* libxl_event_get_domain_death_info returns 1 if death + * event was for this domid */ + if (libxl_event_get_domain_death_info(&priv->ctx, + vm->def->id, + &event, + &info) != 1) + goto cleanup; + + virEventRemoveHandle(watch); + priv->eventHdl = -1; + switch (info.shutdown_reason) { + case SHUTDOWN_poweroff: + case SHUTDOWN_crash: + libxlVmReap(driver, vm, 0); + if (!vm->persistent) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + } + break; + case SHUTDOWN_reboot: + libxlVmReap(driver, vm, 0); + libxlVmStart(driver, vm, 0); + break; + default: + VIR_INFO("Unhandled shutdown_reason %d", info.shutdown_reason); + break; + } + } + +cleanup: + if (vm) + virDomainObjUnlock(vm); + libxl_free_event(&event); +} + +/* + * Register domain events with libxenlight and insert event handles + * in libvirt's event loop. + */ +static int +libxlCreateDomEvents(virDomainObjPtr vm) +{ + libxlDomainObjPrivatePtr priv = vm->privateData; + int fd; + + if (VIR_ALLOC(priv->dWaiter) < 0) { + virReportOOMError(); + return -1; + } + + if (libxl_wait_for_domain_death(&priv->ctx, vm->def->id, priv->dWaiter)) + goto error; + + libxl_get_wait_fd(&priv->ctx, &fd); + if (fd < 0) + goto error; + + priv->waiterFD = fd; + /* Add a reference to the domain object while it is injected in + * the event loop. + */ + virDomainObjRef(vm); + if ((priv->eventHdl = virEventAddHandle( + fd, + VIR_EVENT_HANDLE_READABLE | VIR_EVENT_HANDLE_ERROR, + libxlEventHandler, + vm, libxlDomainObjUnref)) < 0) { + virDomainObjUnref(vm); + goto error; + } + + return 0; + +error: + libxl_free_waiter(priv->dWaiter); + VIR_FREE(priv->dWaiter); + priv->eventHdl = -1; + return -1; +} + +/* + * Start a domain through libxenlight. + * + * virDomainObjPtr should be locked on invocation + */ +static int +libxlVmStart(libxlDriverPrivatePtr driver, + virDomainObjPtr vm, bool start_paused) +{ + libxl_domain_config d_config; + virDomainDefPtr def = vm->def; + int ret; + uint32_t domid = 0; + char *dom_xml = NULL; + pid_t child_console_pid = -1; + libxlDomainObjPrivatePtr priv = vm->privateData; + + memset(&d_config, 0, sizeof(d_config)); + + if (libxlBuildDomainConfig(driver, def, &d_config) < 0 ) + return -1; + + //TODO: Balloon dom0 ?? + //ret = freemem(&d_config->b_info, &d_config->dm_info); + + ret = libxl_domain_create_new(&priv->ctx, &d_config, + NULL, &child_console_pid, &domid); + if (ret) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight failed to create new domain '%s'"), + d_config.c_info.name); + goto error; + } + + def->id = domid; + if ((dom_xml = virDomainDefFormat(def, 0)) == NULL) + goto error; + + if (libxl_userdata_store(&priv->ctx, domid, "libvirt-xml", + (uint8_t *)dom_xml, strlen(dom_xml) + 1)) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight failed to store userdata")); + goto error; + } + + if (libxlCreateDomEvents(vm) < 0) + goto error; + + if (!start_paused) { + libxl_domain_unpause(&priv->ctx, domid); + vm->state = VIR_DOMAIN_RUNNING; + } else { + vm->state = VIR_DOMAIN_PAUSED; + } + + if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) + goto error; + + libxl_domain_config_destroy(&d_config); + VIR_FREE(dom_xml); + return 0; + +error: + if (domid > 0) { + libxl_domain_destroy(&priv->ctx, domid, 0); + def->id = -1; + vm->state = VIR_DOMAIN_SHUTOFF; + } + libxl_domain_config_destroy(&d_config); + VIR_FREE(dom_xml); + return -1; +} + + +/* + * Reconnect to running domains that were previously started/created + * with libxenlight driver. + */ +static void +libxlReconnectDomain(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *opaque) +{ + virDomainObjPtr vm = payload; + libxlDriverPrivatePtr driver = opaque; + int rc; + libxl_dominfo d_info; + int len; + uint8_t *data = NULL; + + virDomainObjLock(vm); + + /* Does domain still exist? */ + rc = libxl_domain_info(&driver->ctx, &d_info, vm->def->id); + if (rc == ERROR_INVAL) { + goto out; + } else if (rc != 0) { + VIR_DEBUG("libxl_domain_info failed (code %d), ignoring domain %d", + rc, vm->def->id); + goto out; + } + + /* Is this a domain that was under libvirt control? */ + if (libxl_userdata_retrieve(&driver->ctx, vm->def->id, + "libvirt-xml", &data, &len)) { + VIR_DEBUG("libxl_userdata_retrieve failed, ignoring domain %d", vm->def->id); + goto out; + } + + /* Update domid in case it changed (e.g. reboot) while we were gone? */ + vm->def->id = d_info.domid; + vm->state = VIR_DOMAIN_RUNNING; + + /* Recreate domain death et. al. events */ + libxlCreateDomEvents(vm); + virDomainObjUnlock(vm); + return; + +out: + libxlVmCleanup(driver, vm); + if (!vm->persistent) + virDomainRemoveInactive(&driver->domains, vm); + else + virDomainObjUnlock(vm); +} + +static void +libxlReconnectDomains(libxlDriverPrivatePtr driver) +{ + virHashForEach(driver->domains.objs, libxlReconnectDomain, driver); +} + +static int +libxlShutdown(void) +{ + if (!libxl_driver) + return -1; + + libxlDriverLock(libxl_driver); + virCapabilitiesFree(libxl_driver->caps); + virDomainObjListDeinit(&libxl_driver->domains); + libxl_ctx_free(&libxl_driver->ctx); + xtl_logger_destroy(libxl_driver->logger); + if (libxl_driver->logger_file) + VIR_FORCE_FCLOSE(libxl_driver->logger_file); + + virBitmapFree(libxl_driver->reservedVNCPorts); + + VIR_FREE(libxl_driver->configDir); + VIR_FREE(libxl_driver->autostartDir); + VIR_FREE(libxl_driver->logDir); + VIR_FREE(libxl_driver->stateDir); + VIR_FREE(libxl_driver->libDir); + VIR_FREE(libxl_driver->saveDir); + + libxlDriverUnlock(libxl_driver); + virMutexDestroy(&libxl_driver->lock); + VIR_FREE(libxl_driver); + + return 0; +} + +static int +libxlStartup(int privileged) { + const libxl_version_info *ver_info; + char *log_file = NULL; + virCommandPtr cmd; + int status; + + /* Disable libxl driver if non-root */ + if (!privileged) { + VIR_INFO0("Not running privileged, disabling libxenlight driver"); + return 0; + } + + /* Disable driver if legacy xen toolstack (xend) is in use */ + cmd = virCommandNewArgList("/usr/sbin/xend", "status", NULL); + if (virCommandRun(cmd, &status) == 0 && status == 0) { + VIR_INFO0("Legacy xen tool stack seems to be in use, disabling " + "libxenlight driver."); + virCommandFree(cmd); + return 0; + } + virCommandFree(cmd); + + if (VIR_ALLOC(libxl_driver) < 0) + return -1; + + if (virMutexInit(&libxl_driver->lock) < 0) { + VIR_ERROR0(_("cannot initialize mutex")); + VIR_FREE(libxl_driver); + return -1; + } + libxlDriverLock(libxl_driver); + + /* Allocate bitmap for vnc port reservation */ + if ((libxl_driver->reservedVNCPorts = + virBitmapAlloc(LIBXL_VNC_PORT_MAX - LIBXL_VNC_PORT_MIN)) == NULL) + goto out_of_memory; + + if (virDomainObjListInit(&libxl_driver->domains) < 0) + goto out_of_memory; + + if (virAsprintf(&libxl_driver->configDir, + "%s", LIBXL_CONFIG_DIR) == -1) + goto out_of_memory; + + if (virAsprintf(&libxl_driver->autostartDir, + "%s", LIBXL_AUTOSTART_DIR) == -1) + goto out_of_memory; + + if (virAsprintf(&libxl_driver->logDir, + "%s", LIBXL_LOG_DIR) == -1) + goto out_of_memory; + + if (virAsprintf(&libxl_driver->stateDir, + "%s", LIBXL_STATE_DIR) == -1) + goto out_of_memory; + + if (virAsprintf(&libxl_driver->libDir, + "%s", LIBXL_LIB_DIR) == -1) + goto out_of_memory; + + if (virAsprintf(&libxl_driver->saveDir, + "%s", LIBXL_SAVE_DIR) == -1) + goto out_of_memory; + + if (virFileMakePath(libxl_driver->logDir) != 0) { + char ebuf[1024]; + VIR_ERROR(_("Failed to create log dir '%s': %s"), + libxl_driver->logDir, virStrerror(errno, ebuf, sizeof ebuf)); + goto error; + } + if (virFileMakePath(libxl_driver->stateDir) != 0) { + char ebuf[1024]; + VIR_ERROR(_("Failed to create state dir '%s': %s"), + libxl_driver->stateDir, virStrerror(errno, ebuf, sizeof ebuf)); + goto error; + } + if (virFileMakePath(libxl_driver->libDir) != 0) { + char ebuf[1024]; + VIR_ERROR(_("Failed to create lib dir '%s': %s"), + libxl_driver->libDir, virStrerror(errno, ebuf, sizeof ebuf)); + goto error; + } + if (virFileMakePath(libxl_driver->saveDir) != 0) { + char ebuf[1024]; + VIR_ERROR(_("Failed to create save dir '%s': %s"), + libxl_driver->saveDir, virStrerror(errno, ebuf, sizeof ebuf)); + goto error; + } + + if (virAsprintf(&log_file, "%s/libxl.log", libxl_driver->logDir) < 0) { + goto out_of_memory; + } + + if ((libxl_driver->logger_file = fopen(log_file, "a")) == NULL) { + virReportSystemError(errno, + _("failed to create logfile %s"), + log_file); + goto error; + } + VIR_FREE(log_file); + + libxl_driver->logger = + (xentoollog_logger *)xtl_createlogger_stdiostream(libxl_driver->logger_file, XTL_DEBUG, 0); + if (!libxl_driver->logger) { + VIR_ERROR0(_("cannot create logger for libxenlight")); + goto error; + } + + if (libxl_ctx_init(&libxl_driver->ctx, + LIBXL_VERSION, + libxl_driver->logger)) { + VIR_ERROR0(_("cannot initialize libxenlight context")); + goto error; + } + + if ((ver_info = libxl_get_version_info(&libxl_driver->ctx)) == NULL) { + VIR_ERROR0(_("cannot version information from libxenlight")); + goto error; + } + libxl_driver->version = (ver_info->xen_version_major * 1000000) + + (ver_info->xen_version_minor * 1000); + + if ((libxl_driver->caps = + libxlMakeCapabilities(&libxl_driver->ctx)) == NULL) { + VIR_ERROR0(_("cannot create capabilities for libxenlight")); + goto error; + } + + libxl_driver->caps->privateDataAllocFunc = libxlDomainObjPrivateAlloc; + libxl_driver->caps->privateDataFreeFunc = libxlDomainObjPrivateFree; + + /* Load running domains first. */ + if (virDomainLoadAllConfigs(libxl_driver->caps, + &libxl_driver->domains, + libxl_driver->stateDir, + libxl_driver->autostartDir, + 1, NULL, NULL) < 0) + goto error; + + libxlReconnectDomains(libxl_driver); + + /* Then inactive persistent configs */ + if (virDomainLoadAllConfigs(libxl_driver->caps, + &libxl_driver->domains, + libxl_driver->configDir, + libxl_driver->autostartDir, + 0, NULL, NULL) < 0) + goto error; + + libxlDriverUnlock(libxl_driver); + + /* TODO: autostart domains */ + + return 0; + +out_of_memory: + virReportOOMError(); +error: + VIR_FREE(log_file); + if (libxl_driver) + libxlDriverUnlock(libxl_driver); + libxlShutdown(); + return -1; +} + +static int +libxlReload(void) +{ + if (!libxl_driver) + return 0; + + libxlDriverLock(libxl_driver); + virDomainLoadAllConfigs(libxl_driver->caps, + &libxl_driver->domains, + libxl_driver->configDir, + libxl_driver->autostartDir, + 0, NULL, libxl_driver); + libxlDriverUnlock(libxl_driver); + + /* TODO: autostart domains */ + + return 0; +} + +static int +libxlActive(void) +{ + if (!libxl_driver) + return 0; + + return 1; +} + +static virDrvOpenStatus +libxlOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED) +{ + if (conn->uri == NULL) { + if (libxl_driver == NULL) + return VIR_DRV_OPEN_DECLINED; + + conn->uri = xmlParseURI("xen:///"); + if (!conn->uri) { + virReportOOMError(); + return VIR_DRV_OPEN_ERROR; + } + } else { + /* Only xen scheme */ + if (conn->uri->scheme == NULL || STRNEQ(conn->uri->scheme, "xen")) + return VIR_DRV_OPEN_DECLINED; + + /* If server name is given, its for remote driver */ + if (conn->uri->server != NULL) + return VIR_DRV_OPEN_DECLINED; + + /* Error if xen or libxl scheme specified but driver not started. */ + if (libxl_driver == NULL) { + libxlError(VIR_ERR_INTERNAL_ERROR, "%s", + _("libxenlight state driver is not active")); + return VIR_DRV_OPEN_ERROR; + } + + /* /session isn't supported in libxenlight */ + if (conn->uri->path && + STRNEQ(conn->uri->path, "") && + STRNEQ(conn->uri->path, "/") && + STRNEQ(conn->uri->path, "/system")) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("unexpected Xen URI path '%s', try xen:///"), + NULLSTR(conn->uri->path)); + return VIR_DRV_OPEN_ERROR; + } + } + + conn->privateData = libxl_driver; + + return VIR_DRV_OPEN_SUCCESS; +}; + +static int +libxlClose(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + conn->privateData = NULL; + return 0; +} + +static const char * +libxlGetType(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + return "xenlight"; +} + +static int +libxlGetVersion(virConnectPtr conn, unsigned long *version) +{ + libxlDriverPrivatePtr driver = conn->privateData; + + libxlDriverLock(driver); + *version = driver->version; + libxlDriverUnlock(driver); + return 0; +} + +static int +libxlGetMaxVcpus(virConnectPtr conn, const char *type ATTRIBUTE_UNUSED) +{ + int ret; + libxlDriverPrivatePtr driver = conn->privateData; + + ret = libxl_get_max_cpus(&driver->ctx); + /* libxl_get_max_cpus() will return 0 if there were any failures, + e.g. xc_physinfo() failing */ + if (ret == 0) + return -1; + + return ret; +} + +static int +libxlNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info) +{ + libxl_physinfo phy_info; + const libxl_version_info* ver_info; + libxlDriverPrivatePtr driver = conn->privateData; + struct utsname utsname; + + if (libxl_get_physinfo(&driver->ctx, &phy_info)) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("libxl_get_physinfo_info failed")); + return -1; + } + + if ((ver_info = libxl_get_version_info(&driver->ctx)) == NULL) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("libxl_get_version_info failed")); + return -1; + } + + uname(&utsname); + if (virStrncpy(info->model, + utsname.machine, + strlen(utsname.machine), + sizeof(info->model)) == NULL) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("machine type %s too big for destination"), + utsname.machine); + return -1; + } + + info->memory = phy_info.total_pages * (ver_info->pagesize / 1024); + info->cpus = phy_info.nr_cpus; + info->nodes = phy_info.nr_nodes; + info->cores = phy_info.cores_per_socket; + info->threads = phy_info.threads_per_core; + info->sockets = 1; + info->mhz = phy_info.cpu_khz / 1000; + return 0; +} + +static char * +libxlGetCapabilities(virConnectPtr conn) +{ + libxlDriverPrivatePtr driver = conn->privateData; + char *xml; + + libxlDriverLock(driver); + if ((xml = virCapabilitiesFormatXML(driver->caps)) == NULL) + virReportOOMError(); + libxlDriverUnlock(driver); + + return xml; +} + +static int +libxlListDomains(virConnectPtr conn, int *ids, int nids) +{ + libxlDriverPrivatePtr driver = conn->privateData; + int n; + + libxlDriverLock(driver); + n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); + libxlDriverUnlock(driver); + + return n; +} + +static int +libxlNumDomains(virConnectPtr conn) +{ + libxlDriverPrivatePtr driver = conn->privateData; + int n; + + libxlDriverLock(driver); + n = virDomainObjListNumOfDomains(&driver->domains, 1); + libxlDriverUnlock(driver); + + return n; +} + +static virDomainPtr +libxlDomainCreateXML(virConnectPtr conn, const char *xml, + unsigned int flags) +{ + libxlDriverPrivatePtr driver = conn->privateData; + virDomainDefPtr def; + virDomainObjPtr vm = NULL; + virDomainPtr dom = NULL; + + virCheckFlags(VIR_DOMAIN_START_PAUSED, NULL); + + libxlDriverLock(driver); + if (!(def = virDomainDefParseString(driver->caps, xml, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0) + goto cleanup; + + if (!(vm = virDomainAssignDef(driver->caps, + &driver->domains, def, false))) + goto cleanup; + def = NULL; + + if (libxlVmStart(driver, vm, (flags & VIR_DOMAIN_START_PAUSED) != 0) < 0) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + +cleanup: + virDomainDefFree(def); + if (vm) + virDomainObjUnlock(vm); + libxlDriverUnlock(driver); + return dom; +} + +static virDomainPtr +libxlDomainLookupByID(virConnectPtr conn, int id) +{ + libxlDriverPrivatePtr driver = conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom = NULL; + + libxlDriverLock(driver); + vm = virDomainFindByID(&driver->domains, id); + libxlDriverUnlock(driver); + + if (!vm) { + libxlError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return dom; +} + +static virDomainPtr +libxlDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid) +{ + libxlDriverPrivatePtr driver = conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom = NULL; + + libxlDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, uuid); + libxlDriverUnlock(driver); + + if (!vm) { + libxlError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return dom; +} + +static virDomainPtr +libxlDomainLookupByName(virConnectPtr conn, const char *name) +{ + libxlDriverPrivatePtr driver = conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom = NULL; + + libxlDriverLock(driver); + vm = virDomainFindByName(&driver->domains, name); + libxlDriverUnlock(driver); + + if (!vm) { + libxlError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return dom; +} + +static int +libxlDomainShutdown(virDomainPtr dom) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + libxlDomainObjPrivatePtr priv; + + libxlDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + libxlError(VIR_ERR_NO_DOMAIN, + _("No domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + libxlError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + if (libxl_domain_shutdown(&priv->ctx, dom->id, LIBXL_DOM_REQ_POWEROFF) != 0) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("Failed to shutdown domain '%d' with libxenlight"), + dom->id); + goto cleanup; + } + + /* vm is marked shutoff (or removed from domains list if not persistent) + * in shutdown event handler. + */ + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + libxlDriverUnlock(driver); + return ret; +} + +static int +libxlDomainReboot(virDomainPtr dom, unsigned int flags ATTRIBUTE_UNUSED) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + libxlDomainObjPrivatePtr priv; + + libxlDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + libxlError(VIR_ERR_NO_DOMAIN, + _("No domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + libxlError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + if (libxl_domain_shutdown(&priv->ctx, dom->id, LIBXL_DOM_REQ_REBOOT) != 0) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("Failed to reboot domain '%d' with libxenlight"), + dom->id); + goto cleanup; + } + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + libxlDriverUnlock(driver); + return ret; +} + +static int +libxlDomainDestroy(virDomainPtr dom) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + libxlDomainObjPrivatePtr priv; + + libxlDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + libxlError(VIR_ERR_NO_DOMAIN, + _("No domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + libxlError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is not running")); + goto cleanup; + } + + priv = vm->privateData; + if (libxlVmReap(driver, vm, 1) != 0) { + libxlError(VIR_ERR_INTERNAL_ERROR, + _("Failed to destroy domain '%d'"), dom->id); + goto cleanup; + } + + if (!vm->persistent) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + } + + ret = 0; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + libxlDriverUnlock(driver); + return ret; +} + +static int +libxlDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + libxlDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + libxlDriverUnlock(driver); + + if (!vm) { + libxlError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + info->state = vm->state; + info->cpuTime = 0; + info->maxMem = vm->def->mem.max_balloon; + info->memory = vm->def->mem.cur_balloon; + info->nrVirtCpu = vm->def->vcpus; + ret = 0; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static char * +libxlDomainDumpXML(virDomainPtr dom, int flags) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr vm; + char *ret = NULL; + + libxlDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + libxlDriverUnlock(driver); + + if (!vm) { + libxlError(VIR_ERR_NO_DOMAIN, "%s", + _("no domain with matching uuid")); + goto cleanup; + } + + ret = virDomainDefFormat(vm->def, flags); + + cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + +static int +libxlListDefinedDomains(virConnectPtr conn, + char **const names, int nnames) +{ + libxlDriverPrivatePtr driver = conn->privateData; + int n; + + libxlDriverLock(driver); + n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); + libxlDriverUnlock(driver); + return n; +} + +static int +libxlNumDefinedDomains(virConnectPtr conn) +{ + libxlDriverPrivatePtr driver = conn->privateData; + int n; + + libxlDriverLock(driver); + n = virDomainObjListNumOfDomains(&driver->domains, 0); + libxlDriverUnlock(driver); + + return n; +} + +static int +libxlDomainCreateWithFlags(virDomainPtr dom, + unsigned int flags ATTRIBUTE_UNUSED) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_START_PAUSED, -1); + + libxlDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + libxlError(VIR_ERR_NO_DOMAIN, + _("No domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + libxlError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is already running")); + goto cleanup; + } + + ret = libxlVmStart(driver, vm, (flags & VIR_DOMAIN_START_PAUSED) != 0); + +cleanup: + if (vm) + virDomainObjUnlock(vm); + libxlDriverUnlock(driver); + return ret; +} + +static int +libxlDomainCreate(virDomainPtr dom) +{ + return libxlDomainCreateWithFlags(dom, 0); +} + +static virDomainPtr +libxlDomainDefineXML(virConnectPtr conn, const char *xml) +{ + libxlDriverPrivatePtr driver = conn->privateData; + virDomainDefPtr def = NULL; + virDomainObjPtr vm = NULL; + virDomainPtr dom = NULL; + int dupVM; + + libxlDriverLock(driver); + if (!(def = virDomainDefParseString(driver->caps, xml, + VIR_DOMAIN_XML_INACTIVE))) + goto cleanup; + + if ((dupVM = virDomainObjIsDuplicate(&driver->domains, def, 0)) < 0) + goto cleanup; + + if (!(vm = virDomainAssignDef(driver->caps, + &driver->domains, def, false))) + goto cleanup; + def = NULL; + vm->persistent = 1; + + if (virDomainSaveConfig(driver->configDir, + vm->newDef ? vm->newDef : vm->def) < 0) { + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + +cleanup: + virDomainDefFree(def); + if (vm) + virDomainObjUnlock(vm); + libxlDriverUnlock(driver); + return dom; +} + +static int +libxlDomainUndefine(virDomainPtr dom) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + libxlDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + virUUIDFormat(dom->uuid, uuidstr); + libxlError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + libxlError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot undefine active domain")); + goto cleanup; + } + + if (!vm->persistent) { + libxlError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot undefine transient domain")); + goto cleanup; + } + + if (virDomainDeleteConfig(driver->configDir, + driver->autostartDir, + vm) < 0) + goto cleanup; + + virDomainRemoveInactive(&driver->domains, vm); + vm = NULL; + ret = 0; + + cleanup: + if (vm) + virDomainObjUnlock(vm); + libxlDriverUnlock(driver); + return ret; +} + +static int +libxlDomainIsActive(virDomainPtr dom) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr obj; + int ret = -1; + + libxlDriverLock(driver); + obj = virDomainFindByUUID(&driver->domains, dom->uuid); + libxlDriverUnlock(driver); + if (!obj) { + libxlError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + ret = virDomainObjIsActive(obj); + + cleanup: + if (obj) + virDomainObjUnlock(obj); + return ret; +} + +static int +libxlDomainIsPersistent(virDomainPtr dom) +{ + libxlDriverPrivatePtr driver = dom->conn->privateData; + virDomainObjPtr obj; + int ret = -1; + + libxlDriverLock(driver); + obj = virDomainFindByUUID(&driver->domains, dom->uuid); + libxlDriverUnlock(driver); + if (!obj) { + libxlError(VIR_ERR_NO_DOMAIN, NULL); + goto cleanup; + } + ret = obj->persistent; + + cleanup: + if (obj) + virDomainObjUnlock(obj); + return ret; +} + + +static virDriver libxlDriver = { + VIR_DRV_LIBXL, + "xenlight", + libxlOpen, /* open */ + libxlClose, /* close */ + NULL, /* supports_feature */ + libxlGetType, /* type */ + libxlGetVersion, /* version */ + NULL, /* libvirtVersion (impl. in libvirt.c) */ + virGetHostname, /* getHostname */ + NULL, /* getSysinfo */ + libxlGetMaxVcpus, /* getMaxVcpus */ + libxlNodeGetInfo, /* nodeGetInfo */ + libxlGetCapabilities, /* getCapabilities */ + libxlListDomains, /* listDomains */ + libxlNumDomains, /* numOfDomains */ + libxlDomainCreateXML, /* domainCreateXML */ + libxlDomainLookupByID, /* domainLookupByID */ + libxlDomainLookupByUUID, /* domainLookupByUUID */ + libxlDomainLookupByName, /* domainLookupByName */ + NULL, /* domainSuspend */ + NULL, /* domainResume */ + libxlDomainShutdown, /* domainShutdown */ + libxlDomainReboot, /* domainReboot */ + libxlDomainDestroy, /* domainDestroy */ + NULL, /* domainGetOSType */ + NULL, /* domainGetMaxMemory */ + NULL, /* domainSetMaxMemory */ + NULL, /* domainSetMemory */ + NULL, /* domainSetMemoryFlags */ + NULL, /* domainSetMemoryParameters */ + NULL, /* domainGetMemoryParameters */ + NULL, /* domainSetBlkioParameters */ + NULL, /* domainGetBlkioParameters */ + libxlDomainGetInfo, /* domainGetInfo */ + NULL, /* domainSave */ + NULL, /* domainRestore */ + NULL, /* domainCoreDump */ + NULL, /* domainSetVcpus */ + NULL, /* domainSetVcpusFlags */ + NULL, /* domainGetVcpusFlags */ + NULL, /* domainPinVcpu */ + NULL, /* domainGetVcpus */ + NULL, /* domainGetMaxVcpus */ + NULL, /* domainGetSecurityLabel */ + NULL, /* nodeGetSecurityModel */ + libxlDomainDumpXML, /* domainDumpXML */ + NULL, /* domainXmlFromNative */ + NULL, /* domainXmlToNative */ + libxlListDefinedDomains, /* listDefinedDomains */ + libxlNumDefinedDomains, /* numOfDefinedDomains */ + libxlDomainCreate, /* domainCreate */ + libxlDomainCreateWithFlags, /* domainCreateWithFlags */ + libxlDomainDefineXML, /* domainDefineXML */ + libxlDomainUndefine, /* domainUndefine */ + NULL, /* domainAttachDevice */ + NULL, /* domainAttachDeviceFlags */ + NULL, /* domainDetachDevice */ + NULL, /* domainDetachDeviceFlags */ + NULL, /* domainUpdateDeviceFlags */ + NULL, /* domainGetAutostart */ + NULL, /* domainSetAutostart */ + NULL, /* domainGetSchedulerType */ + NULL, /* domainGetSchedulerParameters */ + NULL, /* domainSetSchedulerParameters */ + NULL, /* domainMigratePrepare */ + NULL, /* domainMigratePerform */ + NULL, /* domainMigrateFinish */ + NULL, /* domainBlockStats */ + NULL, /* domainInterfaceStats */ + NULL, /* domainMemoryStats */ + NULL, /* domainBlockPeek */ + NULL, /* domainMemoryPeek */ + NULL, /* domainGetBlockInfo */ + NULL, /* nodeGetCellsFreeMemory */ + NULL, /* getFreeMemory */ + NULL, /* domainEventRegister */ + NULL, /* domainEventDeregister */ + NULL, /* domainMigratePrepare2 */ + NULL, /* domainMigrateFinish2 */ + NULL, /* nodeDeviceDettach */ + NULL, /* nodeDeviceReAttach */ + NULL, /* nodeDeviceReset */ + NULL, /* domainMigratePrepareTunnel */ + NULL, /* IsEncrypted */ + NULL, /* IsSecure */ + libxlDomainIsActive, /* DomainIsActive */ + libxlDomainIsPersistent, /* DomainIsPersistent */ + NULL, /* domainIsUpdated */ + NULL, /* cpuCompare */ + NULL, /* cpuBaseline */ + NULL, /* domainGetJobInfo */ + NULL, /* domainAbortJob */ + NULL, /* domainMigrateSetMaxDowntime */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ + NULL, /* domainManagedSave */ + NULL, /* domainHasManagedSaveImage */ + NULL, /* domainManagedSaveRemove */ + NULL, /* domainSnapshotCreateXML */ + NULL, /* domainSnapshotDumpXML */ + NULL, /* domainSnapshotNum */ + NULL, /* domainSnapshotListNames */ + NULL, /* domainSnapshotLookupByName */ + NULL, /* domainHasCurrentSnapshot */ + NULL, /* domainSnapshotCurrent */ + NULL, /* domainRevertToSnapshot */ + NULL, /* domainSnapshotDelete */ + NULL, /* qemuDomainMonitorCommand */ + NULL, /* domainOpenConsole */ +}; + +static virStateDriver libxlStateDriver = { + .name = "LIBXL", + .initialize = libxlStartup, + .cleanup = libxlShutdown, + .reload = libxlReload, + .active = libxlActive, +}; + + +int +libxlRegister(void) +{ + if (virRegisterDriver(&libxlDriver) < 0) + return -1; + if (virRegisterStateDriver(&libxlStateDriver) < 0) + return -1; + + return 0; +} diff --git a/src/libxl/libxl_driver.h b/src/libxl/libxl_driver.h new file mode 100644 index 0000000000..e0475527ce --- /dev/null +++ b/src/libxl/libxl_driver.h @@ -0,0 +1,27 @@ +/*---------------------------------------------------------------------------*/ +/* Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * 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 LIBXL_DRIVER_H +# define LIBXL_DRIVER_H + +# include + +int libxlRegister(void); + +#endif /* LIBXL_DRIVER_H */ diff --git a/src/util/virterror.c b/src/util/virterror.c index 855391398d..160c953c97 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -89,6 +89,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_XENAPI: dom = "XenAPI "; break; + case VIR_FROM_LIBXL: + dom = "xenlight "; + break; case VIR_FROM_XML: dom = "XML "; break; diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 7d08df3296..1162f63521 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -50,6 +50,7 @@ #include "uuid.h" #include "fdstream.h" #include "files.h" +#include "command.h" #define VIR_FROM_THIS VIR_FROM_XEN @@ -229,6 +230,23 @@ xenUnifiedProbe (void) return 0; } +#ifdef WITH_LIBXL +static int +xenUnifiedXendProbe (void) +{ + virCommandPtr cmd; + int status; + int ret = 0; + + cmd = virCommandNewArgList("/usr/sbin/xend", "status", NULL); + if (virCommandRun(cmd, &status) == 0 && status == 0) + ret = 1; + virCommandFree(cmd); + + return ret; +} +#endif + static virDrvOpenStatus xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags) { @@ -292,6 +310,13 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, int flags) } } +#ifdef WITH_LIBXL + /* Decline xen:// URI if xend is not running and libxenlight + * driver is potentially available. */ + if (!xenUnifiedXendProbe()) + return VIR_DRV_OPEN_DECLINED; +#endif + /* We now know the URI is definitely for this driver, so beyond * here, don't return DECLINED, always use ERROR */