On Thu, Jan 23, 2014 at 11:05:26PM +0400, Roman Bogorodskiy wrote:
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.
diff --git a/configure.ac b/configure.ac
index 3a70375..bfbd3a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -524,6 +524,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])
I think it is probably possible to move this into the
LIBVIRT_DRIVER_CHECK_BHYVE macro too. If that doesn't
work then just create a LIBVIRT_DRIVER_ARG_BHYVE macro
for it. Then we have everything in the isolated .m4 file
AC_ARG_WITH([test],
[AS_HELP_STRING([--with-test],
[add test driver support @<:@default=yes@:>@])])
@@ -1031,6 +1035,12 @@ 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.
dnl
@@ -2677,6 +2687,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])
+LIBIVRT_DRIVER_RESULT_BHYVE
AC_MSG_NOTICE([ Test: $with_test])
AC_MSG_NOTICE([ Remote: $with_remote])
AC_MSG_NOTICE([ Network: $with_network])
diff --git a/src/Makefile.am b/src/Makefile.am
index 8f77658..6b95f64 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -515,6 +515,7 @@ DRIVER_SOURCE_FILES = \
$(NULL)
STATEFUL_DRIVER_SOURCE_FILES = \
+ $(BHYVE_DRIVER_SOURCES) \
$(INTERFACE_DRIVER_SOURCES) \
$(LIBXL_DRIVER_SOURCES) \
$(LXC_DRIVER_SOURCES) \
Nit-pick - inconsistent indentation - the other lines use
tabs, but you've used spaces. Although we dislike tabs,
just follow existing practice in this file
@@ -772,6 +773,15 @@ 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
Same indentation note here.
diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
new file mode 100644
index 0000000..f2988cf
--- /dev/null
+++ b/src/bhyve/bhyve_command.c
@@ -0,0 +1,269 @@
+static char*
+virTapGetRealDeviceName(char *name)
Can you aim to use 'virBhyve' as the pefix for absolutely
every function and struct you define in src/bhyve/ files.
We've not been all that good at this in other virt drivers
but its the rough goal we're aiming for is to have all
function prefixes match filename prefix.
+{
+ /* 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");
'*' should associate with the variable rather than the type.
+ if (dirp == NULL) {
+ return NULL;
+ }
virReportSystemError() when this fails.
+
+ 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;
virReportSystemError()
+
+ if (ioctl(fd, TAPGIFNAME, (void *)&ifr) < 0)
+ goto cleanup;
virReportSystemError()
+
+ 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 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 != NULL) {
+ int actualType = virDomainNetGetActualType(net);
+
+ if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
+ if (VIR_STRDUP(brname, virDomainNetGetActualBridgeName(net)) < 0)
+ return -1L;
+ } 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 = virTapGetRealDeviceName(net->ifname);
+
+ if (realifname == NULL) {
+ VIR_FREE(net->ifname);
+ VIR_FREE(brname);
+ return -1;
+ }
+
+ VIR_INFO("%s -> %s", net->ifname, realifname);
s/INFO/DEBUG/
+ /* 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;
+ }
+
+ virCommandAddArg(cmd, "-s");
+ virCommandAddArg(cmd, "0:0,hostbridge");
+ virCommandAddArg(cmd, "-s");
+ virCommandAddArgFormat(cmd, "1:0,virtio-net,%s", realifname);
+
+ 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 = NULL;
+ cmd = virCommandNew(BHYVE);
simpler to merge these onto 1 line.
+
+ /* 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 */
You can look at the 'acpi' features flag to turn this on/off
+ virCommandAddArg(cmd, "-I"); /* Present ioapic to the
guest */
Likewise you can look at the 'apic' features flag to turn this on/off
+ virCommandAddArg(cmd, "-H"); /* vmexit from guest on
hlt */
+ virCommandAddArg(cmd, "-P"); /* vmexit from guest on pause */
What's the functional effect of having these set, or not ?
+
+ /* Devices */
+ if (bhyveBuildNetArgStr(vm->def, cmd) < 0)
+ goto error;
+ if (bhyveBuildDiskArgStr(vm->def, cmd) < 0)
+ goto error;
+ virCommandAddArg(cmd, "-S");
+ virCommandAddArg(cmd, "31,uart");
+ 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 = 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;
+}
diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c
new file mode 100644
index 0000000..5ddb26a
--- /dev/null
+++ b/src/bhyve/bhyve_driver.c
diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c
new file mode 100644
index 0000000..95553d7
--- /dev/null
+++ b/src/bhyve/bhyve_process.c
@@ -0,0 +1,205 @@
+/*
+ * 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 "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 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;
+ }
I'd suggest that can live in just the virBhyveProcessBuildBhyveCmd
method
+int
+virBhyveProcessStop(bhyveConnPtr driver,
+ virDomainObjPtr vm,
+ virDomainShutoffReason reason ATTRIBUTE_UNUSED)
+{
+ int i;
+ int ret = -1;
+ int status;
+ virCommandPtr cmd = NULL;
+
Still want to sanity check with virDomainObjIsActive() before
proceeeding there
+ /* 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;
+}
+
Daniel
--
|:
http://berrange.com -o-
http://www.flickr.com/photos/dberrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|:
http://entangle-photo.org -o-
http://live.gnome.org/gtk-vnc :|