At this point it has a limited functionality and is highly experimental. Supported domain operations are: * define * start * destroy It's only possible to have only one disk device and only one network, which should be of type bridge. --- configure.ac | 37 ++++ daemon/libvirtd.c | 9 + include/libvirt/virterror.h | 1 + po/POTFILES.in | 2 + src/Makefile.am | 35 +++ src/bhyve/bhyve_driver.c | 514 ++++++++++++++++++++++++++++++++++++++++++++ src/bhyve/bhyve_driver.h | 28 +++ src/bhyve/bhyve_process.c | 393 +++++++++++++++++++++++++++++++++ src/bhyve/bhyve_process.h | 36 ++++ src/bhyve/bhyve_utils.h | 48 +++++ src/conf/domain_conf.c | 3 +- src/conf/domain_conf.h | 1 + src/driver.h | 1 + src/libvirt.c | 3 + src/util/virerror.c | 1 + 15 files changed, 1111 insertions(+), 1 deletion(-) create mode 100644 src/bhyve/bhyve_driver.c create mode 100644 src/bhyve/bhyve_driver.h create mode 100644 src/bhyve/bhyve_process.c create mode 100644 src/bhyve/bhyve_process.h create mode 100644 src/bhyve/bhyve_utils.h diff --git a/configure.ac b/configure.ac index 2622dfd..edfe7d4 100644 --- a/configure.ac +++ b/configure.ac @@ -504,6 +504,10 @@ AC_ARG_WITH([parallels], [AS_HELP_STRING([--with-parallels], [add Parallels Cloud Server support @<:@default=check@:>@])]) m4_divert_text([DEFAULTS], [with_parallels=check]) +AC_ARG_WITH([bhyve], + [AS_HELP_STRING([--with-bhyve], + [add BHyVe support @<:@default=check@:>@])]) +m4_divert_text([DEFAULTS], [with_bhyve=check]) AC_ARG_WITH([test], [AS_HELP_STRING([--with-test], [add test driver support @<:@default=yes@:>@])]) @@ -1011,6 +1015,38 @@ fi AM_CONDITIONAL([WITH_PARALLELS], [test "$with_parallels" = "yes"]) dnl +dnl Checks for bhyve driver +dnl + +if test "$with_bhyve" != "no"; then + AC_PATH_PROG([BHYVE], [bhyve], [], [$PATH:/usr/sbin]) + AC_PATH_PROG([BHYVECTL], [bhyvectl], [$PATH:/usr/sbin]) + AC_PATH_PROG([BHYVELOAD], [bhyveload], [$PATH:/usr/sbin/]) + + if test -z "$BHYVE" || test -z "$BHYVECTL" \ + test -z "$BHYVELOAD" || test "$with_freebsd" = "no"; then + if test "$with_bhyve" = "check"; then + with_bhyve="no" + else + AC_MSG_ERROR([The bhyve driver cannot be enabled]) + fi + else + with_bhyve="yes" + fi +fi + +if test "$with_bhyve" = "yes"; then + AC_DEFINE_UNQUOTED([WITH_BHYVE], 1, [whether bhyve driver is enabled]) + AC_DEFINE_UNQUOTED([BHYVE], ["$BHYVE"], + [Location of the bhyve tool]) + AC_DEFINE_UNQUOTED([BHYVECTL], ["$BHYVECTL"], + [Location of the bhyvectl tool]) + AC_DEFINE_UNQUOTED([BHYVELOAD], ["$BHYVELOAD"], + [Location of the bhyveload tool]) +fi +AM_CONDITIONAL([WITH_BHYVE], [test "$with_bhyve" = "yes"]) + +dnl dnl check for shell that understands <> redirection without truncation, dnl needed by src/qemu/qemu_monitor_{text,json}.c. dnl @@ -2582,6 +2618,7 @@ AC_MSG_NOTICE([ PHYP: $with_phyp]) AC_MSG_NOTICE([ ESX: $with_esx]) AC_MSG_NOTICE([ Hyper-V: $with_hyperv]) AC_MSG_NOTICE([Parallels: $with_parallels]) +AC_MSG_NOTICE([ Bhyve: $with_bhyve]) AC_MSG_NOTICE([ Test: $with_test]) AC_MSG_NOTICE([ Remote: $with_remote]) AC_MSG_NOTICE([ Network: $with_network]) diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index 49c42ad..b27c6fd 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -77,6 +77,9 @@ # ifdef WITH_VBOX # include "vbox/vbox_driver.h" # endif +# ifdef WITH_BHYVE +# include "bhyve/bhyve_driver.h" +# endif # ifdef WITH_NETWORK # include "network/bridge_driver.h" # endif @@ -405,6 +408,9 @@ static void daemonInitialize(void) # ifdef WITH_VBOX virDriverLoadModule("vbox"); # endif +# ifdef WITH_BHYVE + virDriverLoadModule("bhyve"); +# endif #else # ifdef WITH_NETWORK networkRegister(); @@ -442,6 +448,9 @@ static void daemonInitialize(void) # ifdef WITH_VBOX vboxRegister(); # endif +# ifdef WITH_BHYVE + bhyveRegister(); +# endif #endif } diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index e31e9c4..7915bbb 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -121,6 +121,7 @@ typedef enum { VIR_FROM_ACCESS = 55, /* Error from access control manager */ VIR_FROM_SYSTEMD = 56, /* Error from systemd code */ + VIR_FROM_BHYVE = 57, /* Error from bhyve driver */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST # endif diff --git a/po/POTFILES.in b/po/POTFILES.in index 49dfc9c..b4f463b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -8,6 +8,8 @@ gnulib/lib/gai_strerror.c gnulib/lib/regcomp.c src/access/viraccessdriverpolkit.c src/access/viraccessmanager.c +src/bhyve/bhyve_driver.c +src/bhyve/bhyve_process.c src/conf/capabilities.c src/conf/cpu_conf.c src/conf/device_conf.c diff --git a/src/Makefile.am b/src/Makefile.am index 57e163f..718bef4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -772,6 +772,13 @@ PARALLELS_DRIVER_SOURCES = \ parallels/parallels_storage.c \ parallels/parallels_network.c +BHYVE_DRIVER_SOURCES = \ + bhyve/bhyve_driver.h \ + bhyve/bhyve_driver.c \ + bhyve/bhyve_process.c \ + bhyve/bhyve_process.h \ + bhyve/bhyve_utils.h + NETWORK_DRIVER_SOURCES = \ network/bridge_driver.h network/bridge_driver.c \ network/bridge_driver_platform.h \ @@ -1308,6 +1315,33 @@ libvirt_driver_parallels_la_CFLAGS = \ libvirt_driver_parallels_la_SOURCES = $(PARALLELS_DRIVER_SOURCES) endif WITH_PARALLELS +if WITH_BHYVE +noinst_LTLIBRARIES += libvirt_driver_bhyve_impl.la +libvirt_driver_bhyve_la_SOURCES = +libvirt_driver_bhyve_la_LIBADD = libvirt_driver_bhyve_impl.la +if WITH_DRIVER_MODULES +mod_LTLIBRARIES += libvirt_driver_bhyve.la +libvirt_driver_bhyve_la_LIBADD += ../gnulib/lib/libgnu.la +libvirt_driver_bhyve_la_LDFLAGS = -module -avoid-version $(AM_LDFLAGS) +else ! WITH_DRIVER_MODULES +noinst_LTLIBRARIES += libvirt_driver_bhyve.la +endif ! WITH_DRIVER_MODULES + +libvirt_driver_bhyve_impl_la_CFLAGS = \ + -I$(top_srcdir)/src/conf \ + $(AM_CFLAGS) +libvirt_driver_bhyve_impl_la_LDFLAGS = $(AM_LDFLAGS) + + + +#noinst_LTLIBRARIES += libvirt_driver_bhyve.la +#libvirt_la_BUILT_LIBADD += libvirt_driver_bhyve.la +#libvirt_driver_bhyve_la_CFLAGS = \ +# -I$(top_srcdir)/src/conf $(AM_CFLAGS) +libvirt_driver_bhyve_impl_la_SOURCES = $(BHYVE_DRIVER_SOURCES) + +endif WITH_BHYVE + if WITH_NETWORK noinst_LTLIBRARIES += libvirt_driver_network_impl.la libvirt_driver_network_la_SOURCES = @@ -1642,6 +1676,7 @@ EXTRA_DIST += \ $(HYPERV_DRIVER_SOURCES) \ $(HYPERV_DRIVER_EXTRA_DIST) \ $(PARALLELS_DRIVER_SOURCES) \ + $(BHYVE_DRIVER_SOURCES) \ $(NETWORK_DRIVER_SOURCES) \ $(INTERFACE_DRIVER_SOURCES) \ $(STORAGE_DRIVER_SOURCES) \ diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c new file mode 100644 index 0000000..ba62d1a --- /dev/null +++ b/src/bhyve/bhyve_driver.c @@ -0,0 +1,514 @@ +/* + * bhyve_driver.c: core driver methods for managing bhyve guests + * + * Copyright (C) 2013 Roman Bogorodskiy + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Roman Bogorodskiy + */ + +#include <config.h> + +#include "virerror.h" +#include "datatypes.h" +#include "virbuffer.h" +#include "viruuid.h" +#include "capabilities.h" +#include "configmake.h" +#include "viralloc.h" +#include "network_conf.h" +#include "interface_conf.h" +#include "domain_audit.h" +#include "domain_conf.h" +#include "snapshot_conf.h" +#include "fdstream.h" +#include "storage_conf.h" +#include "node_device_conf.h" +#include "virxml.h" +#include "virthread.h" +#include "virlog.h" +#include "virfile.h" +#include "virtypedparam.h" +#include "virrandom.h" +#include "virstring.h" +#include "cpu/cpu.h" + +#include "bhyve_driver.h" +#include "bhyve_process.h" +#include "bhyve_utils.h" + +#define VIR_FROM_THIS VIR_FROM_BHYVE + +bhyveConnPtr bhyve_driver = NULL; + +void +bhyveDriverLock(bhyveConnPtr driver) +{ + virMutexLock(&driver->lock); +} + +void +bhyveDriverUnlock(bhyveConnPtr driver) +{ + virMutexUnlock(&driver->lock); +} + +static virCapsPtr +bhyveBuildCapabilities(void) +{ + virCapsPtr caps; + virCapsGuestPtr guest; + + if ((caps = virCapabilitiesNew(virArchFromHost(), + 0, 0)) == NULL) + return NULL; + + if ((guest = virCapabilitiesAddGuest(caps, "hvm", + VIR_ARCH_X86_64, + "bhyve", + NULL, 0, NULL)) == NULL) + goto error; + + if (virCapabilitiesAddGuestDomain(guest, + "bhyve", NULL, NULL, 0, NULL) == NULL) + goto error; + + return caps; + +error: + virObjectUnref(caps); + return NULL; +} + +static char * +bhyveConnectGetCapabilities(virConnectPtr conn) +{ + bhyveConnPtr privconn = conn->privateData; + char *xml; + + bhyveDriverLock(privconn); + if ((xml = virCapabilitiesFormatXML(privconn->caps)) == NULL) + virReportOOMError(); + bhyveDriverUnlock(privconn); + + return xml; +} + +static virDomainObjPtr +bhyveDomObjFromDomain(virDomainPtr domain) +{ + virDomainObjPtr vm; + bhyveConnPtr privconn = domain->conn->privateData; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + vm = virDomainObjListFindByUUID(privconn->domains, domain->uuid); + if (!vm) { + virUUIDFormat(domain->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s' (%s)"), + uuidstr, domain->name); + return NULL; + } + + return vm; +} + +static virDrvOpenStatus +bhyveConnectOpen(virConnectPtr conn, + virConnectAuthPtr auth ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); + + if (!conn->uri) + return VIR_DRV_OPEN_DECLINED; + + if (!conn->uri->scheme || STRNEQ(conn->uri->scheme, "bhyve")) + return VIR_DRV_OPEN_DECLINED; + + if (conn->uri->server) + return VIR_DRV_OPEN_DECLINED; + + if (!STREQ_NULLABLE(conn->uri->path, "/system")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unexpected bhyve URI path '%s', try bhyve:///system"), + conn->uri->path); + return VIR_DRV_OPEN_ERROR; + } + + if (bhyve_driver == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("bhyve state driver is not active")); + return VIR_DRV_OPEN_ERROR; + } + + conn->privateData = bhyve_driver; + + return VIR_DRV_OPEN_SUCCESS; +} + +static int +bhyveConnectClose(virConnectPtr conn) +{ + bhyveConnPtr privconn = conn->privateData; + + bhyveDriverLock(privconn); + virObjectUnref(privconn->caps); + virObjectUnref(privconn->xmlopt); + virObjectUnref(privconn->domains); + conn->privateData = NULL; + + bhyveDriverUnlock(privconn); + virMutexDestroy(&privconn->lock); + + VIR_FREE(privconn); + return 0; +} + +static char * +bhyveConnectGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED) +{ + return virGetHostname(); +} + +static int +bhyveConnectGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long *hvVer) +{ + if (virParseVersionString("0.0.1", hvVer, true) < 0) { + return -1; + } + + return 0; +} + +static int +bhyveDomainGetInfo(virDomainPtr domain ATTRIBUTE_UNUSED, virDomainInfoPtr info ATTRIBUTE_UNUSED) +{ + return 0; +} + +static virDomainPtr +bhyveDomainDefineXML(virConnectPtr conn, const char *xml) +{ + bhyveConnPtr privconn = conn->privateData; + virDomainPtr dom = NULL; + virDomainDefPtr def = NULL; + virDomainDefPtr oldDef = NULL; + virDomainObjPtr vm = NULL; + + bhyveDriverLock(privconn); + + if ((def = virDomainDefParseString(xml, privconn->caps, privconn->xmlopt, + 1 << VIR_DOMAIN_VIRT_BHYVE, + VIR_DOMAIN_XML_INACTIVE)) == NULL) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Can't parse XML desc")); + goto cleanup; + } + + if (!(vm = virDomainObjListAdd(privconn->domains, def, + privconn->xmlopt, + 0, &oldDef))) + goto cleanup; + + VIR_INFO("Creating domain '%s'", vm->def->name); + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) dom->id = vm->def->id; + + if (virDomainSaveConfig(BHYVE_CONFIG_DIR, vm->def) < 0) { + goto cleanup; + } + +cleanup: + virDomainDefFree(def); + if (vm) + virObjectUnlock(vm); + + bhyveDriverUnlock(privconn); + return dom; +} + +static int +bhyveConnectListDomains(virConnectPtr conn, int *ids, int maxids) +{ + bhyveConnPtr privconn = conn->privateData; + int n; + + n = virDomainObjListGetActiveIDs(privconn->domains, ids, maxids, + NULL, NULL); + + return n; +} + +static int +bhyveConnectNumOfDomains(virConnectPtr conn) +{ + bhyveConnPtr privconn = conn->privateData; + int count; + + count = virDomainObjListNumOfDomains(privconn->domains, true, + NULL, NULL); + + return count; +} + +static int +bhyveConnectListDefinedDomains(virConnectPtr conn, char **const names, + int maxnames) +{ + bhyveConnPtr privconn = conn->privateData; + int n; + + memset(names, 0, sizeof(*names) * maxnames); + n = virDomainObjListGetInactiveNames(privconn->domains, names, + maxnames, NULL, NULL); + + return n; +} + +static int +bhyveConnectNumOfDefinedDomains(virConnectPtr conn) +{ + bhyveConnPtr privconn = conn->privateData; + int count; + + count = virDomainObjListNumOfDomains(privconn->domains, false, + NULL, NULL); + + return count; +} + +static int +bhyveConnectListAllDomains(virConnectPtr conn, + virDomainPtr **domains, + unsigned int flags) +{ + bhyveConnPtr privconn = conn->privateData; + int ret = -1; + + virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1); + + ret = virDomainObjListExport(privconn->domains, conn, domains, + NULL, flags); + + return ret; +} + +static virDomainPtr +bhyveDomainLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + bhyveConnPtr privconn = conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom = NULL; + + vm = virDomainObjListFindByUUID(privconn->domains, uuid); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("No domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + +cleanup: + if (vm) + virObjectUnlock(vm); + return dom; +} + +static virDomainPtr bhyveDomainLookupByName(virConnectPtr conn, + const char *name) +{ + bhyveConnPtr privconn = conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom = NULL; + + vm = virDomainObjListFindByName(privconn->domains, name); + + if (!vm) { + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching name '%s'"), name); + goto cleanup; + } + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); + if (dom) + dom->id = vm->def->id; + +cleanup: + if (vm) + virObjectUnlock(vm); + return dom; +} + +static int +bhyveDomainCreate(virDomainPtr dom) +{ + bhyveConnPtr privconn = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + if (!(vm = bhyveDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("Domain is already running")); + goto cleanup; + } + + ret = virBhyveProcessStart(dom->conn, privconn, vm, + VIR_DOMAIN_RUNNING_BOOTED); + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int +bhyveDomainDestroy(virDomainPtr dom) +{ + bhyveConnPtr privconn = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + + if (!(vm = bhyveDomObjFromDomain(dom))) + goto cleanup; + + ret = virBhyveProcessStop(privconn, vm, VIR_DOMAIN_SHUTOFF_DESTROYED); + +cleanup: + if (vm) + virObjectUnlock(vm); + return ret; +} + +static int +bhyveStateCleanup(void) +{ + VIR_INFO("bhyve state cleanup"); + + if (bhyve_driver == NULL) + return -1; + + virObjectUnref(bhyve_driver->domains); + + virMutexDestroy(&bhyve_driver->lock); + VIR_FREE(bhyve_driver); + + return 0; +} + +static int +bhyveStateInitialize(bool priveleged ATTRIBUTE_UNUSED, + virStateInhibitCallback callback ATTRIBUTE_UNUSED, + void *opaque ATTRIBUTE_UNUSED) +{ + if (!priveleged) { + VIR_INFO("Not running priveleged, disabling driver"); + return 0; + } + + if (VIR_ALLOC(bhyve_driver) < 0) { + return -1; + } + + if (virMutexInit(&bhyve_driver->lock) < 0) { + VIR_FREE(bhyve_driver); + return -1; + } + + if (!(bhyve_driver->caps = bhyveBuildCapabilities())) + goto cleanup; + + if (!(bhyve_driver->xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL))) + goto cleanup; + + if (!(bhyve_driver->domains = virDomainObjListNew())) + goto cleanup; + + if (virFileMakePath(BHYVE_LOG_DIR) < 0) { + virReportSystemError(errno, + _("Failed to mkdir %s"), + BHYVE_LOG_DIR); + goto cleanup; + } + + if (virFileMakePath(BHYVE_STATE_DIR) < 0) { + virReportSystemError(errno, + _("Failed to mkdir %s"), + BHYVE_LOG_DIR); + goto cleanup; + } + + if (virDomainObjListLoadAllConfigs(bhyve_driver->domains, + BHYVE_CONFIG_DIR, + NULL, 0, + bhyve_driver->caps, + bhyve_driver->xmlopt, + 1 << VIR_DOMAIN_VIRT_BHYVE, + NULL, NULL) < 0) + goto cleanup; + + return 0; + +cleanup: + bhyveStateCleanup(); + return -1; +} + + +static virDriver bhyveDriver = { + .no = VIR_DRV_BHYVE, + .name = "bhyve", + .connectOpen = bhyveConnectOpen, + .connectClose = bhyveConnectClose, + .connectGetVersion = bhyveConnectGetVersion, + .connectGetHostname = bhyveConnectGetHostname, + .domainGetInfo = bhyveDomainGetInfo, + .connectGetCapabilities = bhyveConnectGetCapabilities, + .connectListDomains = bhyveConnectListDomains, + .connectNumOfDomains = bhyveConnectNumOfDomains, + .connectListAllDomains = bhyveConnectListAllDomains, + .connectListDefinedDomains = bhyveConnectListDefinedDomains, + .connectNumOfDefinedDomains = bhyveConnectNumOfDefinedDomains, + .domainCreate = bhyveDomainCreate, + .domainDestroy = bhyveDomainDestroy, + .domainLookupByUUID = bhyveDomainLookupByUUID, + .domainLookupByName = bhyveDomainLookupByName, + .domainDefineXML = bhyveDomainDefineXML, +}; + + +static virStateDriver bhyveStateDriver = { + .name= "bhyve", + .stateInitialize = bhyveStateInitialize, + .stateCleanup = bhyveStateCleanup, +}; + +int +bhyveRegister(void) +{ + virRegisterDriver(&bhyveDriver); + virRegisterStateDriver(&bhyveStateDriver); + return 0; +} diff --git a/src/bhyve/bhyve_driver.h b/src/bhyve/bhyve_driver.h new file mode 100644 index 0000000..ffe91e5 --- /dev/null +++ b/src/bhyve/bhyve_driver.h @@ -0,0 +1,28 @@ +/* + * bhyve_driver.h: core driver methods for managing bhyve guests + * + * Copyright (C) 2013 Roman Bogorodskiy + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Roman Bogorodskiy <bogorodskiy@xxxxxxxxx> + */ + +#ifndef __BHYVE_DRIVER_H__ +# define __BHYVE_DRIVER_H__ + +int bhyveRegister(void); + +#endif /* __BHYVE_DRIVER_H__ */ diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c new file mode 100644 index 0000000..d481d58 --- /dev/null +++ b/src/bhyve/bhyve_process.c @@ -0,0 +1,393 @@ +/* + * bhyve_process.c: bhyve process management + * + * Copyright (C) 2013 Roman Bogorodskiy + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include <fcntl.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_tap.h> + +#include "bhyve_process.h" +#include "datatypes.h" +#include "virerror.h" +#include "virlog.h" +#include "virfile.h" +#include "viralloc.h" +#include "vircommand.h" +#include "virstring.h" +#include "virpidfile.h" +#include "virprocess.h" +#include "virnetdev.h" +#include "virnetdevbridge.h" +#include "virnetdevtap.h" + +#define VIR_FROM_THIS VIR_FROM_BHYVE + +static char* +virTapGetRealDeviceName(char *name) +{ + /* This is an ugly hack, because if we rename + * tap device to vnet%d, its device name will be + * still /dev/tap%d, and bhyve tries too open /dev/tap%d, + * so we have to find the real name + */ + char *ret = NULL; + struct dirent *dp; + char *devpath = NULL; + int fd; + + DIR* dirp = opendir("/dev"); + if (dirp == NULL) { + return NULL; + } + + while ((dp = readdir(dirp)) != NULL) { + if (STRPREFIX(dp->d_name, "tap")) { + struct ifreq ifr; + if (virAsprintf(&devpath, "/dev/%s", dp->d_name) < 0) { + goto cleanup; + } + if ((fd = open(devpath, O_RDWR)) < 0) + goto cleanup; + + if (ioctl(fd, TAPGIFNAME, (void *)&ifr) < 0) + goto cleanup; + + if (STREQ(name, ifr.ifr_name)) { + /* we can ignore the return value + * because we still have nothing + * to do but return; + */ + ignore_value(VIR_STRDUP(ret, dp->d_name)); + goto cleanup; + } + + VIR_FREE(devpath); + VIR_FORCE_CLOSE(fd); + } + } + +cleanup: + VIR_FREE(devpath); + VIR_FORCE_CLOSE(fd); + closedir(dirp); + return ret; +} + +static virCommandPtr +virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED, + virDomainObjPtr vm) +{ + virCommandPtr cmd = virCommandNew(BHYVECTL); + + virCommandAddArg(cmd, "--destroy"); + virCommandAddArgPair(cmd, "--vm", vm->def->name); + + return cmd; +} + +static virCommandPtr +virBhyveProcessBuildLoadCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED, + virDomainObjPtr vm) +{ + virCommandPtr cmd; + virDomainDiskDefPtr disk = vm->def->disks[0]; + + cmd = virCommandNew(BHYVELOAD); + + /* Memory */ + virCommandAddArg(cmd, "-m"); + virCommandAddArgFormat(cmd, "%llu", + VIR_DIV_UP(vm->def->mem.max_balloon, 1024)); + + /* Image path */ + virCommandAddArg(cmd, "-d"); + virCommandAddArgFormat(cmd, disk->src); + + /* VM name */ + virCommandAddArg(cmd, vm->def->name); + + return cmd; +} + +static virCommandPtr +virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED, + virDomainObjPtr vm) +{ + /* + * /usr/sbin/bhyve -c 2 -m 256 -AI -H -P \ + * -s 0:0,hostbridge \ + * -s 1:0,virtio-net,tap0 \ + * -s 2:0,ahci-hd,${IMG} \ + * -S 31,uart,stdio \ + * vm0 + */ + virCommandPtr cmd = NULL; + virDomainDiskDefPtr disk = vm->def->disks[0]; + virDomainNetDefPtr net = NULL; + char *brname = NULL; + char *realifname = NULL; + int *tapfd = NULL; + + if (vm->def->nnets > 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Domain should have one and only one disk defined")); + return NULL; + } else if (vm->def->nnets == 1) + net = vm->def->nets[0]; + + if (net != NULL) { + int actualType = virDomainNetGetActualType(net); + + if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { + if (VIR_STRDUP(brname, virDomainNetGetActualBridgeName(net)) < 0) + return NULL; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Network type %d is not supported"), + virDomainNetGetActualType(net)); + return NULL; + } + + if (!net->ifname || + STRPREFIX(net->ifname, VIR_NET_GENERATED_PREFIX) || + strchr(net->ifname, '%')) { + VIR_FREE(net->ifname); + if (VIR_STRDUP(net->ifname, VIR_NET_GENERATED_PREFIX "%d") < 0) { + VIR_FREE(brname); + return NULL; + } + } + + if (virNetDevTapCreateInBridgePort(brname, &net->ifname, &net->mac, + vm->def->uuid, tapfd, 1, + virDomainNetGetActualVirtPortProfile(net), + virDomainNetGetActualVlan(net), + VIR_NETDEV_TAP_CREATE_IFUP | VIR_NETDEV_TAP_CREATE_PERSIST) < 0) { + VIR_FREE(net->ifname); + VIR_FREE(brname); + return NULL; + } + } + + realifname = virTapGetRealDeviceName(net->ifname); + + if (realifname == NULL) { + VIR_FREE(net->ifname); + VIR_FREE(brname); + return NULL; + } + + VIR_INFO("%s -> %s", net->ifname, realifname); + /* hack on top of other hack: we need to set + * interface to 'UP' again after re-opening to find its + * name + */ + if (virNetDevSetOnline(net->ifname, true) != 0) { + VIR_FREE(net->ifname); + VIR_FREE(brname); + return NULL; + } + + cmd = virCommandNew(BHYVE); + + /* CPUs */ + virCommandAddArg(cmd, "-c"); + virCommandAddArgFormat(cmd, "%d", vm->def->vcpus); + + /* Memory */ + virCommandAddArg(cmd, "-m"); + virCommandAddArgFormat(cmd, "%llu", + VIR_DIV_UP(vm->def->mem.max_balloon, 1024)); + + /* Options */ + virCommandAddArg(cmd, "-A"); /* Create an ACPI table */ + virCommandAddArg(cmd, "-I"); /* Present ioapic to the guest */ + virCommandAddArg(cmd, "-H"); /* vmexit from guest on hlt */ + virCommandAddArg(cmd, "-P"); /* vmexit from guest on pause */ + + /* Devices */ + virCommandAddArg(cmd, "-s"); + virCommandAddArg(cmd, "0:0,hostbridge"); + virCommandAddArg(cmd, "-s"); + virCommandAddArgFormat(cmd, "1:0,virtio-net,%s", realifname); + virCommandAddArg(cmd, "-s"); + virCommandAddArgFormat(cmd, "2:0,ahci-hd,%s", disk->src); + virCommandAddArg(cmd, "-S"); + virCommandAddArg(cmd, "31,uart"); + virCommandAddArg(cmd, vm->def->name); + + return cmd; +} + +int +virBhyveProcessStart(virConnectPtr conn, + bhyveConnPtr driver, + virDomainObjPtr vm, + virDomainRunningReason reason ATTRIBUTE_UNUSED) +{ + char *logfile = NULL; + int logfd = -1; + off_t pos = -1; + char ebuf[1024]; + virCommandPtr cmd = NULL; + bhyveConnPtr privconn = conn->privateData; + int ret = -1, status; + + if (vm->def->ndisks != 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Domain should have one and only one disk defined")); + return -1; + } + + if (virAsprintf(&logfile, "%s/%s.log", + BHYVE_LOG_DIR, vm->def->name) < 0) + return -1; + + + if ((logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, + S_IRUSR|S_IWUSR)) < 0) { + virReportSystemError(errno, + _("Failed to open '%s'"), + logfile); + goto cleanup; + } + + /* Call bhyveload to load a VM */ + if (!(cmd = virBhyveProcessBuildLoadCmd(driver, + vm))) + goto cleanup; + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); + + /* Log generated command line */ + virCommandWriteArgLog(cmd, logfd); + if ((pos = lseek(logfd, 0, SEEK_END)) < 0) + VIR_WARN("Unable to seek to end of logfile: %s", + virStrerror(errno, ebuf, sizeof(ebuf))); + + VIR_INFO("Loading domain '%s'", vm->def->name); + if (virCommandRun(cmd, &status) < 0) + goto cleanup; + + if (status != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Guest failed to load: %d"), status); + goto cleanup; + } + + virCommandFree(cmd); + + VIR_FREE(privconn->pidfile); + if (!(privconn->pidfile = virPidFileBuildPath(BHYVE_STATE_DIR, + vm->def->name))) { + virReportSystemError(errno, + "%s", _("Failed to build pidfile path.")); + goto cleanup; + } + + if (unlink(privconn->pidfile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Cannot remove state PID file %s"), + privconn->pidfile); + goto cleanup; + } + + /* Call bhyve to start the VM */ + if (!(cmd = virBhyveProcessBuildBhyveCmd(driver, + vm))) + goto cleanup; + virCommandSetOutputFD(cmd, &logfd); + virCommandSetErrorFD(cmd, &logfd); + virCommandWriteArgLog(cmd, logfd); + virCommandSetPidFile(cmd, privconn->pidfile); + virCommandDaemonize(cmd); + + VIR_INFO("Starting domain '%s'", vm->def->name); + ret = virCommandRun(cmd, NULL); + + if (ret == 0) { + if (virPidFileReadPath(privconn->pidfile, &vm->pid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Domain %s didn't show up"), vm->def->name); + goto cleanup; + } + } else { + goto cleanup; + } + +cleanup: + VIR_FREE(logfile); + return ret; +} + +int +virBhyveProcessStop(bhyveConnPtr driver, + virDomainObjPtr vm, + virDomainShutoffReason reason ATTRIBUTE_UNUSED) +{ + size_t i; + int ret = -1; + int status; + virCommandPtr cmd = NULL; + + /* First, try to kill 'bhyve' process */ + if (virProcessKillPainfully(vm->pid, true) != 0) + VIR_WARN("Failed to gracefully stop bhyve VM '%s' (pid: %llu)", + vm->def->name, + (unsigned long long)vm->pid); + + for (i = 0; i < vm->def->nnets; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + int actualType = virDomainNetGetActualType(net); + + if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) { + ignore_value(virNetDevBridgeRemovePort( + virDomainNetGetActualBridgeName(net), + net->ifname)); + ignore_value(virNetDevTapDelete(net->ifname)); + } + } + + /* No matter if shutdown was successful or not, we + * need to unload the VM */ + if (!(cmd = virBhyveProcessBuildDestroyCmd(driver, vm))) + goto cleanup; + + if (virCommandRun(cmd, &status) < 0) + goto cleanup; + + if (status != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Guest failed to stop: %d"), status); + goto cleanup; + } + + ret = 0; + +cleanup: + virCommandFree(cmd); + return ret; +} diff --git a/src/bhyve/bhyve_process.h b/src/bhyve/bhyve_process.h new file mode 100644 index 0000000..70afe0e --- /dev/null +++ b/src/bhyve/bhyve_process.h @@ -0,0 +1,36 @@ +/* + * bhyve_process.h: bhyve process management + * + * Copyright (C) 2013 Roman Bogorodskiy + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __BHYVE_PROCESS_H__ +# define __BHYVE_PROCESS_H__ + +# include "bhyve_utils.h" + +int virBhyveProcessStart(virConnectPtr conn, + bhyveConnPtr driver, + virDomainObjPtr vm, + virDomainRunningReason reason); + +int virBhyveProcessStop(bhyveConnPtr driver, + virDomainObjPtr vm, + virDomainShutoffReason reason); + +#endif /* __BHYVE_PROCESS_H__ */ diff --git a/src/bhyve/bhyve_utils.h b/src/bhyve/bhyve_utils.h new file mode 100644 index 0000000..ed503cd --- /dev/null +++ b/src/bhyve/bhyve_utils.h @@ -0,0 +1,48 @@ +/* + * bhyve_utils.h: bhyve utils + * + * Copyright (C) 2013 Roman Bogorodskiy + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __BHYVE_UTILS_H__ +# define __BHYVE_UTILS_H__ + +# include "driver.h" +# include "domain_conf.h" +# include "configmake.h" +# include "virthread.h" + +# define BHYVE_CONFIG_DIR (SYSCONFDIR "/libvirt/bhyve") +# define BHYVE_STATE_DIR (LOCALSTATEDIR "/run/libvirt/bhyve") +# define BHYVE_LOG_DIR (LOCALSTATEDIR "/log/libvirt/bhyve") + +struct _bhyveConn { + virMutex lock; + virDomainObjListPtr domains; + virCapsPtr caps; + virDomainXMLOptionPtr xmlopt; + char *pidfile; +}; + +typedef struct _bhyveConn bhyveConn; +typedef struct _bhyveConn *bhyveConnPtr; + +void bhyveDriverLock(bhyveConnPtr driver); +void bhyveDriverUnlock(bhyveConnPtr driver); + +#endif /* __BHYVE_UTILS_H__ */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e65f3e3..928f5f5 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -122,7 +122,8 @@ VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST, "hyperv", "vbox", "phyp", - "parallels") + "parallels", + "bhyve") VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST, "fd", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 647d115..c82f598 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -196,6 +196,7 @@ enum virDomainVirtType { VIR_DOMAIN_VIRT_VBOX, VIR_DOMAIN_VIRT_PHYP, VIR_DOMAIN_VIRT_PARALLELS, + VIR_DOMAIN_VIRT_BHYVE, VIR_DOMAIN_VIRT_LAST }; diff --git a/src/driver.h b/src/driver.h index b6927ea..556f91f 100644 --- a/src/driver.h +++ b/src/driver.h @@ -46,6 +46,7 @@ typedef enum { VIR_DRV_LIBXL = 14, VIR_DRV_HYPERV = 15, VIR_DRV_PARALLELS = 16, + VIR_DRV_BHYVE = 17, } virDrvNo; diff --git a/src/libvirt.c b/src/libvirt.c index b14af7e..b493f78 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -96,6 +96,9 @@ #ifdef WITH_PARALLELS # include "parallels/parallels_driver.h" #endif +#ifdef WITH_BHYVE +# include "bhyve/bhyve_driver.h" +#endif #define VIR_FROM_THIS VIR_FROM_NONE diff --git a/src/util/virerror.c b/src/util/virerror.c index d9a9fc4..8f4d1c1 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -124,6 +124,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Access Manager", /* 55 */ "Systemd", + "Bhyve", ) -- 1.8.3.2 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list