bhyve: add a basic driver

At this point it has a limited functionality and is highly
experimental. Supported domain operations are:

  * define
  * start
  * destroy
  * dumpxml
  * dominfo

It's only possible to have only one disk device and only one
network, which should be of type bridge.
This commit is contained in:
Roman Bogorodskiy 2014-02-18 14:08:10 +04:00 committed by Daniel P. Berrange
parent cffa51b81d
commit 0eb4a5f4f1
18 changed files with 1437 additions and 1 deletions

View File

@ -1047,6 +1047,12 @@ if test "$with_parallels" = "yes"; then
fi
AM_CONDITIONAL([WITH_PARALLELS], [test "$with_parallels" = "yes"])
dnl
dnl Checks for bhyve driver
dnl
LIBVIRT_DRIVER_CHECK_BHYVE
dnl
dnl check for shell that understands <> redirection without truncation,
dnl needed by src/qemu/qemu_monitor_{text,json}.c.
@ -2705,6 +2711,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])
LIBVIRT_DRIVER_RESULT_BHYVE
AC_MSG_NOTICE([ Test: $with_test])
AC_MSG_NOTICE([ Remote: $with_remote])
AC_MSG_NOTICE([ Network: $with_network])

View File

@ -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
}

View File

@ -120,6 +120,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

57
m4/virt-driver-bhyve.m4 Normal file
View File

@ -0,0 +1,57 @@
dnl The bhyve driver
dnl
dnl Copyright (C) 2014 Roman Bogorodskiy
dnl
dnl This library is free software; you can redistribute it and/or
dnl modify it under the terms of the GNU Lesser General Public
dnl License as published by the Free Software Foundation; either
dnl version 2.1 of the License, or (at your option) any later version.
dnl
dnl This library is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
dnl Lesser General Public License for more details.
dnl
dnl You should have received a copy of the GNU Lesser General Public
dnl License along with this library. If not, see
dnl <http://www.gnu.org/licenses/>.
dnl
AC_DEFUN([LIBVIRT_DRIVER_CHECK_BHYVE],[
AC_ARG_WITH([bhyve],
[AS_HELP_STRING([--with-bhyve],
[add BHyVe support @<:@default=check@:>@])])
m4_divert_text([DEFAULTS], [with_bhyve=check])
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"])
])
AC_DEFUN([LIBVIRT_DRIVER_RESULT_BHYVE],[
AC_MSG_NOTICE([ Bhyve: $with_bhyve])
])

View File

@ -8,6 +8,9 @@ gnulib/lib/gai_strerror.c
gnulib/lib/regcomp.c
src/access/viraccessdriverpolkit.c
src/access/viraccessmanager.c
src/bhyve/bhyve_command.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

View File

@ -512,6 +512,7 @@ DRIVER_SOURCE_FILES = \
$(NULL)
STATEFUL_DRIVER_SOURCE_FILES = \
$(BHYVE_DRIVER_SOURCES) \
$(INTERFACE_DRIVER_SOURCES) \
$(LIBXL_DRIVER_SOURCES) \
$(LXC_DRIVER_SOURCES) \
@ -772,6 +773,16 @@ PARALLELS_DRIVER_SOURCES = \
parallels/parallels_storage.c \
parallels/parallels_network.c
BHYVE_DRIVER_SOURCES = \
bhyve/bhyve_command.c \
bhyve/bhyve_command.h \
bhyve/bhyve_driver.h \
bhyve/bhyve_driver.c \
bhyve/bhyve_process.c \
bhyve/bhyve_process.h \
bhyve/bhyve_utils.h \
$(NULL)
NETWORK_DRIVER_SOURCES = \
network/bridge_driver.h network/bridge_driver.c \
network/bridge_driver_platform.h \
@ -1308,6 +1319,26 @@ 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/access \
-I$(top_srcdir)/src/conf \
$(AM_CFLAGS)
libvirt_driver_bhyve_impl_la_LDFLAGS = $(AM_LDFLAGS)
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 +1673,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) \

331
src/bhyve/bhyve_command.c Normal file
View File

@ -0,0 +1,331 @@
/*
* bhyve_process.c: bhyve command generation
*
* Copyright (C) 2014 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_command.h"
#include "viralloc.h"
#include "virfile.h"
#include "virstring.h"
#include "virlog.h"
#include "virnetdev.h"
#include "virnetdevbridge.h"
#include "virnetdevtap.h"
#define VIR_FROM_THIS VIR_FROM_BHYVE
static char*
virBhyveTapGetRealDeviceName(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 to 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) {
virReportSystemError(errno,
_("Failed to opendir path '%s'"),
"/dev");
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) {
virReportSystemError(errno, _("Unable to open '%s'"), devpath);
goto cleanup;
}
if (ioctl(fd, TAPGIFNAME, (void *)&ifr) < 0) {
virReportSystemError(errno, "%s",
_("Unable to query tap interface name"));
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);
}
errno = 0;
}
if (errno != 0)
virReportSystemError(errno, "%s",
_("Unable to iterate over TAP devices"));
cleanup:
VIR_FREE(devpath);
VIR_FORCE_CLOSE(fd);
closedir(dirp);
return ret;
}
static int
bhyveBuildNetArgStr(const virDomainDef *def, virCommandPtr cmd)
{
virDomainNetDefPtr net = NULL;
char *brname = NULL;
char *realifname = NULL;
int *tapfd = NULL;
if (def->nnets != 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("domain should have one and only one net defined"));
return -1;
}
net = def->nets[0];
if (net) {
int actualType = virDomainNetGetActualType(net);
if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
if (VIR_STRDUP(brname, virDomainNetGetActualBridgeName(net)) < 0)
return -1;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Network type %d is not supported"),
virDomainNetGetActualType(net));
return -1;
}
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 -1;
}
}
if (virNetDevTapCreateInBridgePort(brname, &net->ifname, &net->mac,
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 -1;
}
}
realifname = virBhyveTapGetRealDeviceName(net->ifname);
if (realifname == NULL) {
VIR_FREE(net->ifname);
VIR_FREE(brname);
return -1;
}
VIR_DEBUG("%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 -1;
}
virCommandAddArgList(cmd, "-s", "0:0,hostbridge", NULL);
virCommandAddArg(cmd, "-s");
virCommandAddArgFormat(cmd, "1:0,virtio-net,%s", realifname);
return 0;
}
static int
bhyveBuildDiskArgStr(const virDomainDef *def, virCommandPtr cmd)
{
virDomainDiskDefPtr disk;
if (def->ndisks != 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("domain should have one and only one disk defined"));
return -1;
}
disk = def->disks[0];
if (disk->bus != VIR_DOMAIN_DISK_BUS_SATA) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unsupported disk bus type"));
return -1;
}
if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unsupported disk device"));
return -1;
}
if (disk->type != VIR_DOMAIN_DISK_TYPE_FILE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unsupported disk type"));
return -1;
}
virCommandAddArg(cmd, "-s");
virCommandAddArgFormat(cmd, "2:0,ahci-hd,%s", disk->src);
return 0;
}
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 = 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 */
if (vm->def->features[VIR_DOMAIN_FEATURE_ACPI] == VIR_DOMAIN_FEATURE_STATE_ON)
virCommandAddArg(cmd, "-A"); /* Create an ACPI table */
if (vm->def->features[VIR_DOMAIN_FEATURE_APIC] == VIR_DOMAIN_FEATURE_STATE_ON)
virCommandAddArg(cmd, "-I"); /* Present ioapic to the guest */
/* Clarification about -H and -P flags from Peter Grehan:
* -H and -P flags force the guest to exit when it executes IA32 HLT and PAUSE
* instructions respectively.
*
* For the HLT exit, bhyve uses that to infer that the guest is idling and can
* be put to sleep until an external event arrives. If this option is not used,
* the guest will always use 100% of CPU on the host.
*
* The PAUSE exit is most useful when there are large numbers of guest VMs running,
* since it forces the guest to exit when it spins on a lock acquisition.
*/
virCommandAddArg(cmd, "-H"); /* vmexit from guest on hlt */
virCommandAddArg(cmd, "-P"); /* vmexit from guest on pause */
/* Devices */
if (bhyveBuildNetArgStr(vm->def, cmd) < 0)
goto error;
if (bhyveBuildDiskArgStr(vm->def, cmd) < 0)
goto error;
virCommandAddArg(cmd, vm->def->name);
return cmd;
error:
virCommandFree(cmd);
return NULL;
}
virCommandPtr
virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
virDomainObjPtr vm)
{
virCommandPtr cmd = virCommandNew(BHYVECTL);
virCommandAddArg(cmd, "--destroy");
virCommandAddArgPair(cmd, "--vm", vm->def->name);
return cmd;
}
virCommandPtr
virBhyveProcessBuildLoadCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
virDomainObjPtr vm)
{
virCommandPtr cmd;
virDomainDiskDefPtr disk;
if (vm->def->ndisks != 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("domain should have one and only one disk defined"));
return NULL;
}
disk = vm->def->disks[0];
if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unsupported disk device"));
return NULL;
}
if (disk->type != VIR_DOMAIN_DISK_TYPE_FILE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unsupported disk type"));
return NULL;
}
cmd = virCommandNew(BHYVELOAD);
/* Memory */
virCommandAddArg(cmd, "-m");
virCommandAddArgFormat(cmd, "%llu",
VIR_DIV_UP(vm->def->mem.max_balloon, 1024));
/* Image path */
virCommandAddArg(cmd, "-d");
virCommandAddArg(cmd, disk->src);
/* VM name */
virCommandAddArg(cmd, vm->def->name);
return cmd;
}

41
src/bhyve/bhyve_command.h Normal file
View File

@ -0,0 +1,41 @@
/*
* bhyve_process.c: bhyve command generation
*
* Copyright (C) 2014 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_COMMAND_H__
# define __BHYVE_COMMAND_H__
# include "bhyve_utils.h"
# include "domain_conf.h"
# include "vircommand.h"
virCommandPtr virBhyveProcessBuildBhyveCmd(bhyveConnPtr,
virDomainObjPtr vm);
virCommandPtr
virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver,
virDomainObjPtr vm);
virCommandPtr
virBhyveProcessBuildLoadCmd(bhyveConnPtr driver,
virDomainObjPtr vm);
#endif /* __BHYVE_COMMAND_H__ */

612
src/bhyve/bhyve_driver.c Normal file
View File

@ -0,0 +1,612 @@
/*
* bhyve_driver.c: core driver methods for managing bhyve guests
*
* Copyright (C) 2014 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 <sys/utsname.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 "viraccessapicheck.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;
if (virConnectGetCapabilitiesEnsureACL(conn) < 0)
return NULL;
if ((xml = virCapabilitiesFormatXML(privconn->caps)) == NULL)
virReportOOMError();
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 == NULL) {
if (bhyve_driver == NULL)
return VIR_DRV_OPEN_DECLINED;
if (!(conn->uri = virURIParse("bhyve:///system")))
return VIR_DRV_OPEN_ERROR;
} else {
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;
}
}
if (virConnectOpenEnsureACL(conn) < 0)
return VIR_DRV_OPEN_ERROR;
conn->privateData = bhyve_driver;
return VIR_DRV_OPEN_SUCCESS;
}
static int
bhyveConnectClose(virConnectPtr conn)
{
conn->privateData = NULL;
return 0;
}
static char *
bhyveConnectGetHostname(virConnectPtr conn ATTRIBUTE_UNUSED)
{
if (virConnectGetHostnameEnsureACL(conn) < 0)
return NULL;
return virGetHostname();
}
static int
bhyveConnectGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long *version)
{
struct utsname ver;
if (virConnectGetVersionEnsureACL(conn) < 0)
return -1;
uname(&ver);
if (virParseVersionString(ver.release, version, true) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown release: %s"), ver.release);
return -1;
}
return 0;
}
static int
bhyveDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
{
virDomainObjPtr vm;
int ret = -1;
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainGetInfoEnsureACL(domain->conn, vm->def) < 0)
goto cleanup;
info->state = virDomainObjGetState(vm, NULL);
info->maxMem = vm->def->mem.max_balloon;
info->nrVirtCpu = vm->def->vcpus;
ret = 0;
cleanup:
virObjectUnlock(vm);
return ret;
}
static int
bhyveDomainGetState(virDomainPtr domain,
int *state,
int *reason,
unsigned int flags)
{
virDomainObjPtr vm;
int ret = -1;
virCheckFlags(0, -1);
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainGetStateEnsureACL(domain->conn, vm->def) < 0)
goto cleanup;
*state = virDomainObjGetState(vm, reason);
ret = 0;
cleanup:
virObjectUnlock(vm);
return ret;
}
static char *
bhyveDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
{
virDomainObjPtr vm;
char *ret = NULL;
if (!(vm = bhyveDomObjFromDomain(domain)))
goto cleanup;
if (virDomainGetXMLDescEnsureACL(domain->conn, vm->def, flags) < 0)
goto cleanup;
ret = virDomainDefFormat(vm->def, flags);
cleanup:
virObjectUnlock(vm);
return ret;
}
static virDomainPtr
bhyveDomainDefineXML(virConnectPtr conn, const char *xml)
{
bhyveConnPtr privconn = conn->privateData;
virDomainPtr dom = NULL;
virDomainDefPtr def = NULL;
virDomainDefPtr oldDef = NULL;
virDomainObjPtr vm = NULL;
if ((def = virDomainDefParseString(xml, privconn->caps, privconn->xmlopt,
1 << VIR_DOMAIN_VIRT_BHYVE,
VIR_DOMAIN_XML_INACTIVE)) == NULL)
goto cleanup;
if (virDomainDefineXMLEnsureACL(conn, def) < 0)
goto cleanup;
if (!(vm = virDomainObjListAdd(privconn->domains, def,
privconn->xmlopt,
0, &oldDef)))
goto cleanup;
def = NULL;
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);
virObjectUnlock(vm);
return dom;
}
static int
bhyveConnectListDomains(virConnectPtr conn, int *ids, int maxids)
{
bhyveConnPtr privconn = conn->privateData;
int n;
if (virConnectListDomainsEnsureACL(conn) < 0)
return -1;
n = virDomainObjListGetActiveIDs(privconn->domains, ids, maxids,
virConnectListDomainsCheckACL, conn);
return n;
}
static int
bhyveConnectNumOfDomains(virConnectPtr conn)
{
bhyveConnPtr privconn = conn->privateData;
int count;
if (virConnectNumOfDomainsEnsureACL(conn) < 0)
return -1;
count = virDomainObjListNumOfDomains(privconn->domains, true,
virConnectNumOfDomainsCheckACL, conn);
return count;
}
static int
bhyveConnectListDefinedDomains(virConnectPtr conn, char **const names,
int maxnames)
{
bhyveConnPtr privconn = conn->privateData;
int n;
if (virConnectListDefinedDomainsEnsureACL(conn) < 0)
return -1;
memset(names, 0, sizeof(*names) * maxnames);
n = virDomainObjListGetInactiveNames(privconn->domains, names,
maxnames, virConnectListDefinedDomainsCheckACL, conn);
return n;
}
static int
bhyveConnectNumOfDefinedDomains(virConnectPtr conn)
{
bhyveConnPtr privconn = conn->privateData;
int count;
if (virConnectNumOfDefinedDomainsEnsureACL(conn) < 0)
return -1;
count = virDomainObjListNumOfDomains(privconn->domains, false,
virConnectNumOfDefinedDomainsCheckACL, conn);
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);
if (virConnectListAllDomainsEnsureACL(conn) < 0)
return -1;
ret = virDomainObjListExport(privconn->domains, conn, domains,
virConnectListAllDomainsCheckACL, 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;
}
if (virDomainLookupByUUIDEnsureACL(conn, vm->def) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom)
dom->id = vm->def->id;
cleanup:
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;
}
if (virDomainLookupByNameEnsureACL(conn, vm->def) < 0)
goto cleanup;
dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
if (dom)
dom->id = vm->def->id;
cleanup:
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 (virDomainCreateEnsureACL(dom->conn, vm->def) < 0)
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:
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;
if (virDomainDestroyEnsureACL(dom->conn, vm->def) < 0)
goto cleanup;
ret = virBhyveProcessStop(privconn, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
cleanup:
virObjectUnlock(vm);
return ret;
}
static int
bhyveStateCleanup(void)
{
VIR_DEBUG("bhyve state cleanup");
if (bhyve_driver == NULL)
return -1;
virObjectUnref(bhyve_driver->domains);
virObjectUnref(bhyve_driver->caps);
virObjectUnref(bhyve_driver->xmlopt);
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, /* 1.2.2 */
.connectClose = bhyveConnectClose, /* 1.2.2 */
.connectGetVersion = bhyveConnectGetVersion, /* 1.2.2 */
.connectGetHostname = bhyveConnectGetHostname, /* 1.2.2 */
.domainGetInfo = bhyveDomainGetInfo, /* 1.2.2 */
.domainGetState = bhyveDomainGetState, /* 1.2.2 */
.connectGetCapabilities = bhyveConnectGetCapabilities, /* 1.2.2 */
.connectListDomains = bhyveConnectListDomains, /* 1.2.2 */
.connectNumOfDomains = bhyveConnectNumOfDomains, /* 1.2.2 */
.connectListAllDomains = bhyveConnectListAllDomains, /* 1.2.2 */
.connectListDefinedDomains = bhyveConnectListDefinedDomains, /* 1.2.2 */
.connectNumOfDefinedDomains = bhyveConnectNumOfDefinedDomains, /* 1.2.2 */
.domainCreate = bhyveDomainCreate, /* 1.2.2 */
.domainDestroy = bhyveDomainDestroy, /* 1.2.2 */
.domainLookupByUUID = bhyveDomainLookupByUUID, /* 1.2.2 */
.domainLookupByName = bhyveDomainLookupByName, /* 1.2.2 */
.domainDefineXML = bhyveDomainDefineXML, /* 1.2.2 */
.domainGetXMLDesc = bhyveDomainGetXMLDesc, /* 1.2.2 */
};
static virStateDriver bhyveStateDriver = {
.name = "bhyve",
.stateInitialize = bhyveStateInitialize,
.stateCleanup = bhyveStateCleanup,
};
int
bhyveRegister(void)
{
virRegisterDriver(&bhyveDriver);
virRegisterStateDriver(&bhyveStateDriver);
return 0;
}

28
src/bhyve/bhyve_driver.h Normal file
View File

@ -0,0 +1,28 @@
/*
* bhyve_driver.h: core driver methods for managing bhyve guests
*
* Copyright (C) 2014 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@gmail.com>
*/
#ifndef __BHYVE_DRIVER_H__
# define __BHYVE_DRIVER_H__
int bhyveRegister(void);
#endif /* __BHYVE_DRIVER_H__ */

224
src/bhyve/bhyve_process.c Normal file
View File

@ -0,0 +1,224 @@
/*
* bhyve_process.c: bhyve process management
*
* Copyright (C) 2014 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 <sys/ioctl.h>
#include <net/if.h>
#include <net/if_tap.h>
#include "bhyve_process.h"
#include "bhyve_command.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
int
virBhyveProcessStart(virConnectPtr conn,
bhyveConnPtr driver,
virDomainObjPtr vm,
virDomainRunningReason reason)
{
char *logfile = NULL;
int logfd = -1;
off_t pos = -1;
char ebuf[1024];
virCommandPtr cmd = NULL;
virCommandPtr load_cmd = NULL;
bhyveConnPtr privconn = conn->privateData;
int ret = -1, status;
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;
}
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);
/* Now bhyve command is constructed, meaning the
* domain is ready to be started, so we can build
* and execute bhyveload command */
if (!(load_cmd = virBhyveProcessBuildLoadCmd(driver, vm)))
goto cleanup;
virCommandSetOutputFD(load_cmd, &logfd);
virCommandSetErrorFD(load_cmd, &logfd);
/* Log generated command line */
virCommandWriteArgLog(load_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_DEBUG("Loading domain '%s'", vm->def->name);
if (virCommandRun(load_cmd, &status) < 0)
goto cleanup;
if (status != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Guest failed to load: %d"), status);
goto cleanup;
}
/* Now we can start the domain */
VIR_DEBUG("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;
}
vm->def->id = vm->pid;
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
} else {
goto cleanup;
}
cleanup:
if (ret < 0) {
virCommandPtr destroy_cmd;
if ((destroy_cmd = virBhyveProcessBuildDestroyCmd(driver, vm)) != NULL) {
virCommandSetOutputFD(load_cmd, &logfd);
virCommandSetErrorFD(load_cmd, &logfd);
ignore_value(virCommandRun(destroy_cmd, NULL));
virCommandFree(destroy_cmd);
}
}
virCommandFree(load_cmd);
virCommandFree(cmd);
VIR_FREE(logfile);
VIR_FORCE_CLOSE(logfd);
return ret;
}
int
virBhyveProcessStop(bhyveConnPtr driver,
virDomainObjPtr vm,
virDomainShutoffReason reason ATTRIBUTE_UNUSED)
{
size_t i;
int ret = -1;
int status;
virCommandPtr cmd = NULL;
if (!virDomainObjIsActive(vm)) {
VIR_DEBUG("VM '%s' not active", vm->def->name);
return 0;
}
if (vm->pid <= 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid PID %d for VM"),
(int)vm->pid);
return -1;
}
/* First, try to kill 'bhyve' process */
if (virProcessKillPainfully(vm->pid, true) != 0)
VIR_WARN("Failed to gracefully stop bhyve VM '%s' (pid: %d)",
vm->def->name,
(int)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;
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
vm->pid = -1;
vm->def->id = -1;
cleanup:
virCommandFree(cmd);
return ret;
}

36
src/bhyve/bhyve_process.h Normal file
View File

@ -0,0 +1,36 @@
/*
* bhyve_process.h: bhyve process management
*
* Copyright (C) 2014 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__ */

48
src/bhyve/bhyve_utils.h Normal file
View File

@ -0,0 +1,48 @@
/*
* bhyve_utils.h: bhyve utils
*
* Copyright (C) 2014 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__ */

View File

@ -123,7 +123,8 @@ VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST,
"hyperv",
"vbox",
"phyp",
"parallels")
"parallels",
"bhyve")
VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST,
"fd",

View File

@ -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
};

View File

@ -47,6 +47,7 @@ typedef enum {
VIR_DRV_LIBXL = 14,
VIR_DRV_HYPERV = 15,
VIR_DRV_PARALLELS = 16,
VIR_DRV_BHYVE = 17,
} virDrvNo;

View File

@ -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

View File

@ -124,6 +124,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
"Access Manager", /* 55 */
"Systemd",
"Bhyve",
)